Ver código fonte

Merging with olympia

conectado 4 anos atrás
pai
commit
96be4d9acb
72 arquivos alterados com 2818 adições e 2090 exclusões
  1. 8 4
      Cargo.lock
  2. 3 3
      node/src/chain_spec/initial_members.rs
  3. 3 3
      node/src/chain_spec/mod.rs
  4. 4 1
      runtime-modules/common/Cargo.toml
  5. 33 1
      runtime-modules/common/src/lib.rs
  6. 16 1
      runtime-modules/common/src/working_group.rs
  7. 1 1
      runtime-modules/council/Cargo.toml
  8. 16 2
      runtime-modules/council/src/mock.rs
  9. 5 9
      runtime-modules/forum/src/lib.rs
  10. 16 3
      runtime-modules/governance/src/mock.rs
  11. 6 4
      runtime-modules/membership/Cargo.toml
  12. 6 4
      runtime-modules/membership/src/genesis.rs
  13. 364 257
      runtime-modules/membership/src/lib.rs
  14. 0 132
      runtime-modules/membership/src/mock.rs
  15. 0 272
      runtime-modules/membership/src/tests.rs
  16. 265 0
      runtime-modules/membership/src/tests/fixtures.rs
  17. 313 0
      runtime-modules/membership/src/tests/mock.rs
  18. 406 0
      runtime-modules/membership/src/tests/mod.rs
  19. 1 2
      runtime-modules/proposals/codex/Cargo.toml
  20. 167 467
      runtime-modules/proposals/codex/src/lib.rs
  21. 16 10
      runtime-modules/proposals/codex/src/tests/mock.rs
  22. 370 339
      runtime-modules/proposals/codex/src/tests/mod.rs
  23. 23 3
      runtime-modules/proposals/codex/src/types.rs
  24. 6 3
      runtime-modules/proposals/discussion/Cargo.toml
  25. 13 9
      runtime-modules/proposals/discussion/src/benchmarking.rs
  26. 11 10
      runtime-modules/proposals/discussion/src/lib.rs
  27. 16 3
      runtime-modules/proposals/discussion/src/tests/mock.rs
  28. 3 2
      runtime-modules/proposals/engine/Cargo.toml
  29. 21 12
      runtime-modules/proposals/engine/src/benchmarking.rs
  30. 52 30
      runtime-modules/proposals/engine/src/lib.rs
  31. 18 8
      runtime-modules/proposals/engine/src/tests/mock/mod.rs
  32. 48 12
      runtime-modules/proposals/engine/src/tests/mod.rs
  33. 5 7
      runtime-modules/proposals/engine/src/types/mod.rs
  34. 2 2
      runtime-modules/service-discovery/Cargo.toml
  35. 2 2
      runtime-modules/service-discovery/src/lib.rs
  36. 16 6
      runtime-modules/service-discovery/src/mock.rs
  37. 3 3
      runtime-modules/staking-handler/Cargo.toml
  38. 4 4
      runtime-modules/staking-handler/src/lib.rs
  39. 16 3
      runtime-modules/staking-handler/src/mock.rs
  40. 1 4
      runtime-modules/staking-handler/src/test.rs
  41. 1 2
      runtime-modules/storage/Cargo.toml
  42. 1 1
      runtime-modules/storage/src/data_directory.rs
  43. 3 4
      runtime-modules/storage/src/lib.rs
  44. 18 3
      runtime-modules/storage/src/tests/mock.rs
  45. 9 4
      runtime-modules/working-group/Cargo.toml
  46. 13 15
      runtime-modules/working-group/src/benchmarking.rs
  47. 7 18
      runtime-modules/working-group/src/checks.rs
  48. 17 9
      runtime-modules/working-group/src/lib.rs
  49. 24 38
      runtime-modules/working-group/src/tests/fixtures.rs
  50. 8 8
      runtime-modules/working-group/src/tests/hiring_workflow.rs
  51. 7 8
      runtime-modules/working-group/src/tests/mock.rs
  52. 25 52
      runtime-modules/working-group/src/tests/mod.rs
  53. 7 16
      runtime-modules/working-group/src/types.rs
  54. 1 2
      runtime/Cargo.toml
  55. 17 0
      runtime/src/constants.rs
  56. 2 1
      runtime/src/integration/proposals/council_origin_validator.rs
  57. 1 3
      runtime/src/integration/proposals/membership_origin_validator.rs
  58. 1 1
      runtime/src/integration/proposals/mod.rs
  59. 2 1
      runtime/src/integration/proposals/proposal_encoder.rs
  60. 38 11
      runtime/src/lib.rs
  61. 27 3
      runtime/src/runtime_api.rs
  62. 12 7
      runtime/src/tests/mod.rs
  63. 46 31
      runtime/src/tests/proposals_integration/mod.rs
  64. 188 90
      runtime/src/tests/proposals_integration/working_group_proposals.rs
  65. 11 27
      runtime/src/weights/frame_system.rs
  66. 2 1
      runtime/src/weights/mod.rs
  67. 2 2
      runtime/src/weights/pallet_constitution.rs
  68. 0 34
      runtime/src/weights/pallet_im_online.rs
  69. 3 20
      runtime/src/weights/pallet_session.rs
  70. 3 20
      runtime/src/weights/pallet_utility.rs
  71. 42 19
      scripts/generate-weights.sh
  72. 1 1
      utils/chain-spec-builder/Cargo.toml

+ 8 - 4
Cargo.lock

@@ -694,7 +694,7 @@ dependencies = [
 
 [[package]]
 name = "chain-spec-builder"
-version = "3.1.1"
+version = "4.0.0"
 dependencies = [
  "ansi_term 0.12.1",
  "enum-utils",
@@ -2327,7 +2327,7 @@ dependencies = [
 
 [[package]]
 name = "joystream-node-runtime"
-version = "7.8.0"
+version = "8.0.0"
 dependencies = [
  "frame-benchmarking",
  "frame-executive",
@@ -3729,13 +3729,14 @@ dependencies = [
 
 [[package]]
 name = "pallet-common"
-version = "3.1.0"
+version = "4.0.0"
 dependencies = [
  "frame-support",
  "frame-system",
  "pallet-timestamp",
  "parity-scale-codec",
  "serde",
+ "sp-arithmetic",
  "sp-runtime",
  "strum 0.19.5",
  "strum_macros 0.19.4",
@@ -3778,6 +3779,7 @@ dependencies = [
  "frame-support",
  "frame-system",
  "pallet-balances",
+ "pallet-common",
  "pallet-membership",
  "pallet-referendum",
  "pallet-staking-handler",
@@ -3890,13 +3892,15 @@ dependencies = [
 
 [[package]]
 name = "pallet-membership"
-version = "3.1.0"
+version = "4.0.0"
 dependencies = [
  "frame-support",
  "frame-system",
  "pallet-balances",
  "pallet-common",
+ "pallet-staking-handler",
  "pallet-timestamp",
+ "pallet-working-group",
  "parity-scale-codec",
  "serde",
  "sp-arithmetic",

+ 3 - 3
node/src/chain_spec/initial_members.rs

@@ -1,13 +1,13 @@
-use node_runtime::{membership, AccountId, Moment};
+use node_runtime::{membership, AccountId};
 use std::{fs, path::Path};
 
 /// Generates a Vec of genesis members parsed from a json file
-pub fn from_json(data_file: &Path) -> Vec<membership::genesis::Member<u64, AccountId, Moment>> {
+pub fn from_json(data_file: &Path) -> Vec<membership::genesis::Member<u64, AccountId>> {
     let data = fs::read_to_string(data_file).expect("Failed reading file");
     serde_json::from_str(&data).expect("failed parsing members data")
 }
 
 /// Generates an empty Vec of genesis members
-pub fn none() -> Vec<membership::genesis::Member<u64, AccountId, Moment>> {
+pub fn none() -> Vec<membership::genesis::Member<u64, AccountId>> {
     vec![]
 }

+ 3 - 3
node/src/chain_spec/mod.rs

@@ -32,8 +32,8 @@ use node_runtime::{
     membership, wasm_binary_unwrap, AuthorityDiscoveryConfig, BabeConfig, Balance, BalancesConfig,
     ContentDirectoryConfig, CouncilConfig, CouncilElectionConfig, DataObjectStorageRegistryConfig,
     DataObjectTypeRegistryConfig, ElectionParameters, ForumConfig, GrandpaConfig, ImOnlineConfig,
-    MembersConfig, Moment, SessionConfig, SessionKeys, Signature, StakerStatus, StakingConfig,
-    SudoConfig, SystemConfig, DAYS,
+    MembersConfig, SessionConfig, SessionKeys, Signature, StakerStatus, StakingConfig, SudoConfig,
+    SystemConfig, DAYS,
 };
 
 // Exported to be used by chain-spec-builder
@@ -207,7 +207,7 @@ pub fn testnet_genesis(
     )>,
     root_key: AccountId,
     endowed_accounts: Vec<AccountId>,
-    members: Vec<membership::genesis::Member<u64, AccountId, Moment>>,
+    members: Vec<membership::genesis::Member<u64, AccountId>>,
     forum_config: ForumConfig,
     initial_balances: Vec<(AccountId, Balance)>,
 ) -> GenesisConfig {

+ 4 - 1
runtime-modules/common/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-common'
-version = '3.1.0'
+version = '4.0.0'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -13,6 +13,8 @@ sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://
 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'}
 pallet-timestamp = { package = 'pallet-timestamp', 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'}
+
 
 [features]
 default = ['std']
@@ -25,4 +27,5 @@ std = [
 	'frame-support/std',
 	'frame-system/std',
 	'pallet-timestamp/std',
+	'sp-arithmetic/std',
 ]

+ 33 - 1
runtime-modules/common/src/lib.rs

@@ -6,9 +6,41 @@ pub mod currency;
 pub mod origin;
 pub mod working_group;
 
-use codec::{Decode, Encode};
+use codec::{Codec, Decode, Encode};
+use frame_support::Parameter;
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
+use sp_arithmetic::traits::BaseArithmetic;
+use sp_runtime::traits::{MaybeSerialize, Member};
+
+/// Member id type alias
+pub type MemberId<T> = <T as Trait>::MemberId;
+
+/// Actor id type alias
+pub type ActorId<T> = <T as Trait>::ActorId;
+
+/// Generic trait for membership dependent pallets.
+pub trait Trait: frame_system::Trait {
+    /// Describes the common type for the members.
+    type MemberId: Parameter
+        + Member
+        + BaseArithmetic
+        + Codec
+        + Default
+        + Copy
+        + MaybeSerialize
+        + PartialEq;
+
+    /// Describes the common type for the working group members (workers).
+    type ActorId: Parameter
+        + Member
+        + BaseArithmetic
+        + Codec
+        + Default
+        + Copy
+        + MaybeSerialize
+        + PartialEq;
+}
 
 /// Defines time in both block number and substrate time abstraction.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]

+ 16 - 1
runtime-modules/common/src/working_group.rs

@@ -1,6 +1,7 @@
 use codec::{Decode, Encode};
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
+use sp_runtime::DispatchResult;
 #[cfg(feature = "std")]
 use strum_macros::EnumIter;
 
@@ -12,6 +13,20 @@ pub enum WorkingGroup {
     Forum,
     /// Storage working group: working_group::Instance2.
     Storage,
-    /// Storage working group: working_group::Instance3.
+    /// Content directory working group: working_group::Instance3.
     Content,
+    /// Membership working group: working_group::Instance4.
+    Membership,
+}
+
+/// Working group interface to use in the in the pallets with working groups.
+pub trait WorkingGroupIntegration<T: crate::Trait> {
+    fn ensure_worker_origin(origin: T::Origin, worker_id: &T::ActorId) -> DispatchResult;
+
+    // TODO: Implement or remove during the Forum refactoring to this interface
+    // /// Defines whether the member is the leader of the working group.
+    // fn is_working_group_leader(member_id: &T::MemberId) -> bool;
+    //
+    // /// Defines whether the member is the worker of the working group.
+    // fn is_working_group_member(member_id: &T::MemberId) -> bool;
 }

+ 1 - 1
runtime-modules/council/Cargo.toml

@@ -22,8 +22,8 @@ balances = { package = 'pallet-balances', default-features = false, git = 'https
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 rand = "0.7.3"
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-
 membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
+common = { package = 'pallet-common', default-features = false, path = '../common'}
 
 [features]
 default = ['std']

+ 16 - 2
runtime-modules/council/src/mock.rs

@@ -9,6 +9,7 @@ use crate::{
 };
 
 use balances;
+use frame_support::dispatch::DispatchResult;
 use frame_support::traits::{Currency, Get, LockIdentifier, OnFinalize};
 use frame_support::{
     impl_outer_event, impl_outer_origin, parameter_types, StorageMap, StorageValue,
@@ -72,6 +73,11 @@ parameter_types! {
     pub const BudgetRefillPeriod: u64 = 1000;
 }
 
+impl common::Trait for Runtime {
+    type MemberId = u64;
+    type ActorId = u64;
+}
+
 impl Trait for Runtime {
     type Event = TestEvent;
 
@@ -284,9 +290,17 @@ impl balances::Trait for Runtime {
 
 impl membership::Trait for Runtime {
     type Event = TestEvent;
-    type MemberId = u64;
-    type ActorId = u64;
     type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
+}
+
+impl common::working_group::WorkingGroupIntegration<Runtime> for () {
+    fn ensure_worker_origin(
+        _origin: <Runtime as frame_system::Trait>::Origin,
+        _worker_id: &<Runtime as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        unimplemented!();
+    }
 }
 
 impl pallet_timestamp::Trait for Runtime {

+ 5 - 9
runtime-modules/forum/src/lib.rs

@@ -595,9 +595,6 @@ decl_module! {
 
             Self::ensure_can_create_thread(account_id, &forum_user_id, &category_id)?;
 
-            // Check that thread can be added to category
-            Self::ensure_category_is_mutable(&category_id)?;
-
             // Ensure poll is valid
             if let Some(ref data) = poll {
                 // Check all poll alternatives
@@ -615,7 +612,7 @@ decl_module! {
             let new_thread_id = <NextThreadId<T>>::get();
 
             // Add inital post to thread
-            let _ = Self::add_new_post(category_id, new_thread_id, &text, forum_user_id);
+            let _ = Self::add_new_post(new_thread_id, &text, forum_user_id);
 
             // Build a new thread
             let new_thread = Thread {
@@ -844,7 +841,10 @@ decl_module! {
             //
 
             // Add new post
-            let (post_id, _) = Self::add_new_post(thread.category_id, thread_id, text.as_slice(), forum_user_id);
+            let (post_id, _) = Self::add_new_post(thread_id, text.as_slice(), forum_user_id);
+
+            // Update thread's post counter
+            <ThreadById<T>>::mutate(thread.category_id, thread_id, |c| c.num_direct_posts += 1);
 
             // Generate event
             Self::deposit_event(RawEvent::PostAdded(post_id));
@@ -956,7 +956,6 @@ decl_module! {
 
 impl<T: Trait> Module<T> {
     pub fn add_new_post(
-        category_id: T::CategoryId,
         thread_id: T::ThreadId,
         text: &[u8],
         author_id: T::ForumUserId,
@@ -977,9 +976,6 @@ impl<T: Trait> Module<T> {
         // Update next post id
         <NextPostId<T>>::mutate(|n| *n += One::one());
 
-        // Update thread's post counter
-        <ThreadById<T>>::mutate(category_id, thread_id, |c| c.num_direct_posts += 1);
-
         (new_post_id, new_post)
     }
 

+ 16 - 3
runtime-modules/governance/src/mock.rs

@@ -9,7 +9,7 @@ use sp_core::H256;
 use sp_runtime::{
     testing::Header,
     traits::{BlakeTwo256, IdentityLookup},
-    BuildStorage, Perbill,
+    BuildStorage, DispatchResult, Perbill,
 };
 
 impl_outer_origin! {
@@ -72,12 +72,25 @@ impl election::Trait for Test {
 
     type CouncilElected = (Council,);
 }
+impl common::Trait for Test {
+    type MemberId = u64;
+    type ActorId = u64;
+}
 impl membership::Trait for Test {
     type Event = ();
-    type MemberId = u64;
-    type ActorId = u32;
     type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
 }
+
+impl common::working_group::WorkingGroupIntegration<Test> for () {
+    fn ensure_worker_origin(
+        _origin: <Test as frame_system::Trait>::Origin,
+        _worker_id: &<Test as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        unimplemented!();
+    }
+}
+
 impl minting::Trait for Test {
     type Currency = Balances;
     type MintId = u64;

+ 6 - 4
runtime-modules/membership/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-membership'
-version = '3.1.0'
+version = '4.0.0'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -13,12 +13,14 @@ frame-system = { package = 'frame-system', default-features = false, git = 'http
 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'}
 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'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+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'}
+staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../staking-handler'}
+working-group = { package = 'pallet-working-group', default-features = false, path = '../working-group'}
 
 [features]
 default = ['std']
@@ -31,6 +33,6 @@ std = [
 	'sp-arithmetic/std',
 	'sp-runtime/std',
 	'pallet-timestamp/std',
-	'common/std',
 	'balances/std',
-]
+	'common/std',
+]

+ 6 - 4
runtime-modules/membership/src/genesis.rs

@@ -1,17 +1,19 @@
+//! Membership genesis module.
+
 #![cfg(feature = "std")]
 
 use crate::{GenesisConfig, Trait};
 use serde::{Deserialize, Serialize};
 
 #[derive(Clone, Serialize, Deserialize)]
-pub struct Member<MemberId, AccountId, Moment> {
+pub struct Member<MemberId, AccountId> {
     pub member_id: MemberId,
     pub root_account: AccountId,
     pub controller_account: AccountId,
     pub handle: String,
     pub avatar_uri: String,
     pub about: String,
-    pub registered_at_time: Moment,
+    pub name: String,
 }
 
 /// Builder fo membership module genesis configuration.
@@ -33,7 +35,7 @@ impl<T: Trait> GenesisConfigBuilder<T> {
     }
 
     /// Generates a Vec of `Member`s from pairs of MemberId and AccountId
-    fn generate_mock_members(&self) -> Vec<Member<T::MemberId, T::AccountId, T::Moment>> {
+    fn generate_mock_members(&self) -> Vec<Member<T::MemberId, T::AccountId>> {
         self.members
             .iter()
             .enumerate()
@@ -45,7 +47,7 @@ impl<T: Trait> GenesisConfigBuilder<T> {
                 handle: (10000 + ix).to_string(),
                 avatar_uri: "".into(),
                 about: "".into(),
-                registered_at_time: T::Moment::from(0),
+                name: "".into(),
             })
             .collect()
     }

+ 364 - 257
runtime-modules/membership/src/lib.rs

@@ -1,51 +1,74 @@
+//! Joystream membership module.
+//!
+//! Memberships are the stable identifier under which actors occupy roles,
+//! submit proposals and communicate on the platform.
+//!
+//! ### Overview
+//! A membership is a representation of an actor on the platform,
+//! and it exist to serve the profile and reputation purposes.
+//!
+//! #### Profile
+//! A membership has an associated rich profile that includes information that support presenting
+//! the actor in a human friendly way in applications, much more so than raw accounts in isolation.
+//!
+//! #### Reputation
+//!
+//! Facilitates the consolidation of all activity under one stable identifier,
+//! allowing an actor to invest in the reputation of a membership through prolonged participation
+//! with good conduct. This gives honest and competent actors a practical way to signal quality,
+//! and this quality signal is a key screening parameter allowing entry into more important and
+//! sensitive activities. While nothing technically prevents an actor from registering for multiple
+//! memberships, the value of doing a range of activities under one membership should be greater
+//! than having it fragmented, since reputation, in essence, increases with the length and scope of
+//! the history of consistent good conduct.
+//!
+//! It's important to be aware that a membership is not an account, but a higher level concept that
+//! involves accounts for authentication. The membership subsystem is responsible for storing and
+//! managing all memberships on the platform, as well as enabling the creation of new memberships,
+//! and the terms under which this may happen.
+//!
+//! Supported extrinsics:
+//! - [update_profile](./struct.Module.html#method.update_profile) - updates profile parameters.
+//! - [buy_membership](./struct.Module.html#method.buy_membership) - allows to buy membership
+//! for non-members.
+//! - [update_accounts](./struct.Module.html#method.update_accounts) - updates member accounts.
+//! - [update_profile_verification](./struct.Module.html#method.update_profile_verification) -
+//! updates member profile verification status.
+//! - [set_referral_cut](./struct.Module.html#method.set_referral_cut) - updates the referral cut.
+//!
+//! [Joystream handbook description](https://joystream.gitbook.io/joystream-handbook/subsystems/membership)
+
 // Ensure we're `no_std` when compiling for Wasm.
 #![cfg_attr(not(feature = "std"), no_std)]
 
 pub mod genesis;
-pub(crate) mod mock;
 mod tests;
 
-use codec::{Codec, Decode, Encode};
+use codec::{Decode, Encode};
 use frame_support::traits::{Currency, Get};
-use frame_support::{decl_event, decl_module, decl_storage, ensure, Parameter};
+use frame_support::{decl_error, decl_event, decl_module, decl_storage, ensure};
+use frame_system::ensure_root;
 use frame_system::ensure_signed;
-use sp_arithmetic::traits::{BaseArithmetic, One};
-use sp_runtime::traits::{MaybeSerialize, Member};
+use sp_arithmetic::traits::{One, Zero};
 use sp_std::borrow::ToOwned;
 use sp_std::vec::Vec;
 
+use common::working_group::WorkingGroupIntegration;
+
 // Balance type alias
 type BalanceOf<T> = <T as balances::Trait>::Balance;
 
-//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>;
-
-pub trait Trait: frame_system::Trait + balances::Trait + pallet_timestamp::Trait {
+pub trait Trait:
+    frame_system::Trait + balances::Trait + pallet_timestamp::Trait + common::Trait
+{
+    /// Membership module event type.
     type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
 
-    type MemberId: Parameter
-        + Member
-        + BaseArithmetic
-        + Codec
-        + Default
-        + Copy
-        + MaybeSerialize
-        + PartialEq;
-
-    /// Describes the common type for the working group members (workers).
-    type ActorId: Parameter
-        + Member
-        + BaseArithmetic
-        + Codec
-        + Default
-        + Copy
-        + MaybeSerialize
-        + PartialEq
-        + Ord;
-
     /// Defines the default membership fee.
     type MembershipFee: Get<BalanceOf<Self>>;
+
+    /// Working group pallet integration.
+    type WorkingGroup: common::working_group::WorkingGroupIntegration<Self>;
 }
 
 // Default user info constraints
@@ -53,35 +76,26 @@ const DEFAULT_MIN_HANDLE_LENGTH: u32 = 5;
 const DEFAULT_MAX_HANDLE_LENGTH: u32 = 40;
 const DEFAULT_MAX_AVATAR_URI_LENGTH: u32 = 1024;
 const DEFAULT_MAX_ABOUT_TEXT_LENGTH: u32 = 2048;
+const DEFAULT_MAX_NAME_LENGTH: u32 = 200;
 
-/// Public membership object alias.
-pub type Membership<T> = MembershipObject<
-    <T as frame_system::Trait>::BlockNumber,
-    <T as pallet_timestamp::Trait>::Moment,
-    <T as frame_system::Trait>::AccountId,
->;
+/// Public membership profile alias.
+pub type Membership<T> = MembershipObject<<T as frame_system::Trait>::AccountId>;
 
 #[derive(Encode, Decode, Default)]
-/// Stored information about a registered user
-pub struct MembershipObject<BlockNumber, Moment, AccountId> {
-    /// The unique handle chosen by member
+/// Stored information about a registered user.
+pub struct MembershipObject<AccountId> {
+    /// User name.
+    pub name: Vec<u8>,
+
+    /// The unique handle chosen by member.
     pub handle: Vec<u8>,
 
-    /// A Url to member's Avatar image
+    /// A Url to member's Avatar image.
     pub avatar_uri: Vec<u8>,
 
-    /// Short text chosen by member to share information about themselves
+    /// Short text chosen by member to share information about themselves.
     pub about: Vec<u8>,
 
-    /// Block number when member was registered
-    pub registered_at_block: BlockNumber,
-
-    /// Timestamp when member was registered
-    pub registered_at_time: Moment,
-
-    /// How the member was registered
-    pub entry: EntryMethod,
-
     /// Member's root account id. Only the root account is permitted to set a new root account
     /// and update the controller account. Other modules may only allow certain actions if
     /// signed with root account. It is intended to be an account that can remain offline and
@@ -92,26 +106,86 @@ pub struct MembershipObject<BlockNumber, Moment, AccountId> {
     /// a member to act under their identity in other modules. It will usually be used more
     /// online and will have less funds in its balance.
     pub controller_account: AccountId,
+
+    /// An indicator that reflects whether the implied real world identity in the profile
+    /// corresponds to the true actor behind the membership.
+    pub verified: bool,
 }
 
 // Contains valid or default user details
 struct ValidatedUserInfo {
+    name: Vec<u8>,
     handle: Vec<u8>,
     avatar_uri: Vec<u8>,
     about: Vec<u8>,
 }
 
-#[derive(Encode, Decode, Debug, PartialEq)]
-pub enum EntryMethod {
-    Paid,
-    Genesis,
+/// Parameters for the buy_membership extrinsic.
+#[derive(Encode, Decode, Default, Clone, PartialEq, Debug)]
+pub struct BuyMembershipParameters<AccountId, MemberId> {
+    /// New member root account.
+    pub root_account: AccountId,
+
+    /// New member controller account.
+    pub controller_account: AccountId,
+
+    /// New member user name.
+    pub name: Option<Vec<u8>>,
+
+    /// New member handle.
+    pub handle: Option<Vec<u8>>,
+
+    /// New member avatar URI.
+    pub avatar_uri: Option<Vec<u8>>,
+
+    /// New member 'about' text.
+    pub about: Option<Vec<u8>>,
+
+    /// Referrer member id.
+    pub referrer_id: Option<MemberId>,
 }
 
-/// 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 EntryMethod {
-    fn default() -> Self {
-        Self::Genesis
+decl_error! {
+    /// Membership module predefined errors
+    pub enum Error for Module<T: Trait> {
+        /// New members not allowed.
+        NewMembersNotAllowed,
+
+        /// Not enough balance to buy membership.
+        NotEnoughBalanceToBuyMembership,
+
+        /// Controller account required.
+        ControllerAccountRequired,
+
+        /// Root account required.
+        RootAccountRequired,
+
+        /// Invalid origin.
+        UnsignedOrigin,
+
+        /// Member profile not found (invalid member id).
+        MemberProfileNotFound,
+
+        /// Handle already registered.
+        HandleAlreadyRegistered,
+
+        /// Handle too short.
+        HandleTooShort,
+
+        /// Handle too long.
+        HandleTooLong,
+
+        /// Avatar uri too long.
+        AvatarUriTooLong,
+
+        /// Name too long.
+        NameTooLong,
+
+        /// Handle must be provided during registration.
+        HandleMustBeProvidedDuringRegistration,
+
+        /// Cannot find a membership for a provided referrer id.
+        ReferrerIsNotMember,
     }
 }
 
@@ -121,7 +195,7 @@ decl_storage! {
         /// total number of members created. MemberIds start at Zero.
         pub NextMemberId get(fn members_created) : T::MemberId;
 
-        /// Mapping of member's id to their membership profile
+        /// Mapping of member's id to their membership profile.
         pub MembershipById get(fn membership) : map hasher(blake2_128_concat)
             T::MemberId => Membership<T>;
 
@@ -129,30 +203,41 @@ decl_storage! {
         pub(crate) MemberIdsByRootAccountId : map hasher(blake2_128_concat)
             T::AccountId => Vec<T::MemberId>;
 
-        /// Mapping of a controller account id to vector of member ids it controls
+        /// Mapping of a controller account id to vector of member ids it controls.
         pub(crate) MemberIdsByControllerAccountId : map hasher(blake2_128_concat)
             T::AccountId => Vec<T::MemberId>;
 
-        /// Registered unique handles and their mapping to their owner
+        /// Registered unique handles and their mapping to their owner.
         pub MemberIdByHandle get(fn handles) : map hasher(blake2_128_concat)
             Vec<u8> => T::MemberId;
 
-        /// Is the platform is accepting new members or not
+        /// Is the platform is accepting new members or not.
         pub NewMembershipsAllowed get(fn new_memberships_allowed) : bool = true;
 
-        // User Input Validation parameters - do these really need to be state variables
-        // I don't see a need to adjust these in future?
+        /// Minimum allowed handle length.
         pub MinHandleLength get(fn min_handle_length) : u32 = DEFAULT_MIN_HANDLE_LENGTH;
+
+        /// Maximum allowed handle length.
         pub MaxHandleLength get(fn max_handle_length) : u32 = DEFAULT_MAX_HANDLE_LENGTH;
+
+        /// Maximum allowed avatar URI length.
         pub MaxAvatarUriLength get(fn max_avatar_uri_length) : u32 = DEFAULT_MAX_AVATAR_URI_LENGTH;
+
+        /// Maximum allowed 'about' text length.
         pub MaxAboutTextLength get(fn max_about_text_length) : u32 = DEFAULT_MAX_ABOUT_TEXT_LENGTH;
 
+        /// Maximum allowed name length.
+        pub MaxNameLength get(fn max_name_length) : u32 = DEFAULT_MAX_NAME_LENGTH;
+
+        /// Referral cut to receive during on buying the membership.
+        pub ReferralCut get(fn referral_cut) : BalanceOf<T>;
     }
     add_extra_genesis {
-        config(members) : Vec<genesis::Member<T::MemberId, T::AccountId, T::Moment>>;
+        config(members) : Vec<genesis::Member<T::MemberId, T::AccountId>>;
         build(|config: &GenesisConfig<T>| {
             for member in &config.members {
                 let checked_user_info = <Module<T>>::check_user_registration_info(
+                    Some(member.name.clone().into_bytes()),
                     Some(member.handle.clone().into_bytes()),
                     Some(member.avatar_uri.clone().into_bytes()),
                     Some(member.about.clone().into_bytes())
@@ -162,13 +247,8 @@ decl_storage! {
                     &member.root_account,
                     &member.controller_account,
                     &checked_user_info,
-                    EntryMethod::Genesis,
-                    T::BlockNumber::from(1),
-                    member.registered_at_time
                 ).expect("Importing Member Failed");
 
-
-
                 // ensure imported member id matches assigned id
                 assert_eq!(member_id, member.member_id, "Import Member Failed: MemberId Incorrect");
             }
@@ -178,15 +258,14 @@ decl_storage! {
 
 decl_event! {
     pub enum Event<T> where
-      <T as frame_system::Trait>::AccountId,
-      <T as Trait>::MemberId,
+      <T as common::Trait>::MemberId,
+      Balance = BalanceOf<T>,
     {
-        MemberRegistered(MemberId, AccountId),
-        MemberUpdatedAboutText(MemberId),
-        MemberUpdatedAvatar(MemberId),
-        MemberUpdatedHandle(MemberId),
-        MemberSetRootAccount(MemberId, AccountId),
-        MemberSetControllerAccount(MemberId, AccountId),
+        MemberRegistered(MemberId),
+        MemberProfileUpdated(MemberId),
+        MemberAccountsUpdated(MemberId),
+        MemberVerificationStatusUpdated(MemberId, bool),
+        ReferralCutUpdated(Balance),
     }
 }
 
@@ -197,184 +276,237 @@ decl_module! {
         /// Exports const - membership fee.
         const MembershipFee: BalanceOf<T> = T::MembershipFee::get();
 
-        /// Non-members can buy membership
+        /// Non-members can buy membership.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn buy_membership(
             origin,
-            handle: Option<Vec<u8>>,
-            avatar_uri: Option<Vec<u8>>,
-            about: Option<Vec<u8>>
+            params: BuyMembershipParameters<T::AccountId, T::MemberId>
         ) {
             let who = ensure_signed(origin)?;
 
-            // make sure we are accepting new memberships
-            ensure!(Self::new_memberships_allowed(), "new members not allowed");
+            // Make sure we are accepting new memberships.
+            ensure!(Self::new_memberships_allowed(), Error::<T>::NewMembersNotAllowed);
 
             let fee = T::MembershipFee::get();
 
-            // ensure enough free balance to cover terms fees
+            // Ensure enough free balance to cover terms fees.
             ensure!(
                 balances::Module::<T>::usable_balance(&who) >= fee,
-                "not enough balance to buy membership"
+                Error::<T>::NotEnoughBalanceToBuyMembership
             );
 
-            let user_info = Self::check_user_registration_info(handle, avatar_uri, about)?;
+            // Verify user parameters.
+            let user_info = Self::check_user_registration_info(
+                params.name,
+                params.handle,
+                params.avatar_uri,
+                params.about)
+            ?;
+
+            let referrer = params
+                .referrer_id
+                .map(|referrer_id| {
+                    Self::ensure_membership_with_error(referrer_id, Error::<T>::ReferrerIsNotMember)
+                })
+                .transpose()?;
+
+            //
+            // == MUTATION SAFE ==
+            //
 
             let member_id = Self::insert_member(
-                &who,
-                &who,
+                &params.root_account,
+                &params.controller_account,
                 &user_info,
-                EntryMethod::Paid,
-                <frame_system::Module<T>>::block_number(),
-                <pallet_timestamp::Module<T>>::now()
             )?;
 
+            // Collect membership fee (just burn it).
             let _ = balances::Module::<T>::slash(&who, fee);
 
-            Self::deposit_event(RawEvent::MemberRegistered(member_id, who));
-        }
-
-        /// Change member's about text
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn change_member_about_text(origin, member_id: T::MemberId, text: Vec<u8>) {
-            let sender = ensure_signed(origin)?;
-
-            let membership = Self::ensure_membership(member_id)?;
-
-            ensure!(membership.controller_account == sender, "only controller account can update member about text");
-
-            Self::_change_member_about_text(member_id, &text)?;
-        }
-
-        /// Change member's avatar
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn change_member_avatar(origin, member_id: T::MemberId, uri: Vec<u8>) {
-            let sender = ensure_signed(origin)?;
-
-            let membership = Self::ensure_membership(member_id)?;
+            // Reward the referring member.
+            if let Some(referrer) = referrer{
+                let referral_cut: BalanceOf<T> = Self::get_referral_bonus();
 
-            ensure!(membership.controller_account == sender, "only controller account can update member avatar");
-
-            Self::_change_member_avatar(member_id, &uri)?;
-        }
-
-        /// Change member's handle. Will ensure new handle is unique and old one will be available
-        /// for other members to use.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn change_member_handle(origin, member_id: T::MemberId, handle: Vec<u8>) {
-            let sender = ensure_signed(origin)?;
-
-            let membership = Self::ensure_membership(member_id)?;
-
-            ensure!(membership.controller_account == sender, "only controller account can update member handle");
+                if referral_cut > Zero::zero() {
+                    let _ = balances::Module::<T>::deposit_creating(
+                        &referrer.controller_account,
+                        referral_cut
+                    );
+                }
+            }
 
-            Self::_change_member_handle(member_id, handle)?;
+            // Fire the event.
+            Self::deposit_event(RawEvent::MemberRegistered(member_id));
         }
 
-        /// Update member's all or some of handle, avatar and about text.
+        /// Update member's all or some of name, handle, avatar and about text.
+        /// No effect if no changed fields.
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn update_membership(
+        pub fn update_profile(
             origin,
             member_id: T::MemberId,
+            name: Option<Vec<u8>>,
             handle: Option<Vec<u8>>,
             avatar_uri: Option<Vec<u8>>,
             about: Option<Vec<u8>>
         ) {
-            let sender = ensure_signed(origin)?;
+            // No effect if no changes.
+            if name.is_none() && handle.is_none() && avatar_uri.is_none() && about.is_none() {
+                return Ok(())
+            }
 
-            let membership = Self::ensure_membership(member_id)?;
+            Self::ensure_member_controller_account_signed(origin, &member_id)?;
+
+            let mut membership = Self::ensure_membership(member_id)?;
 
-            ensure!(membership.controller_account == sender, "only controller account can update member info");
+            // Prepare for possible handle change;
+            let old_handle = membership.handle.clone();
+            let mut new_handle: Option<Vec<u8>> = None;
 
+            // Update fields if needed
             if let Some(uri) = avatar_uri {
-                Self::_change_member_avatar(member_id, &uri)?;
+                Self::validate_avatar(&uri)?;
+                membership.avatar_uri = uri;
+            }
+            if let Some(name) = name {
+                Self::validate_name(&name)?;
+                membership.name = name;
             }
             if let Some(about) = about {
-                Self::_change_member_about_text(member_id, &about)?;
+                let text = Self::validate_text(&about);
+                membership.about = text;
             }
             if let Some(handle) = handle {
-                Self::_change_member_handle(member_id, handle)?;
+                Self::validate_handle(&handle)?;
+                Self::ensure_unique_handle(&handle)?;
+
+                new_handle = Some(handle.clone());
+                membership.handle = handle;
+            }
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            <MembershipById<T>>::insert(member_id, membership);
+
+            if let Some(new_handle) = new_handle {
+                <MemberIdByHandle<T>>::remove(&old_handle);
+                <MemberIdByHandle<T>>::insert(new_handle, member_id);
             }
+
+            Self::deposit_event(RawEvent::MemberProfileUpdated(member_id));
         }
 
+        /// Updates member root or controller accounts. No effect if both new accounts are empty.
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn set_controller_account(origin, member_id: T::MemberId, new_controller_account: T::AccountId) {
-            let sender = ensure_signed(origin)?;
+        pub fn update_accounts(
+            origin,
+            member_id: T::MemberId,
+            new_root_account: Option<T::AccountId>,
+            new_controller_account: Option<T::AccountId>,
+        ) {
+            // No effect if no changes.
+            if new_root_account.is_none() && new_controller_account.is_none() {
+                return Ok(())
+            }
 
+            let sender = ensure_signed(origin)?;
             let mut membership = Self::ensure_membership(member_id)?;
 
-            ensure!(membership.root_account == sender, "only root account can set new controller account");
+            ensure!(membership.root_account == sender, Error::<T>::RootAccountRequired);
 
-            // only update if new_controller_account is different than current one
-            if membership.controller_account != new_controller_account {
-                <MemberIdsByControllerAccountId<T>>::mutate(&membership.controller_account, |ids| {
+            //
+            // == MUTATION SAFE ==
+            //
+
+            if let Some(root_account) = new_root_account {
+                <MemberIdsByRootAccountId<T>>::mutate(&membership.root_account, |ids| {
                     ids.retain(|id| *id != member_id);
                 });
 
-                <MemberIdsByControllerAccountId<T>>::mutate(&new_controller_account, |ids| {
+                <MemberIdsByRootAccountId<T>>::mutate(&root_account, |ids| {
                     ids.push(member_id);
                 });
 
-                membership.controller_account = new_controller_account.clone();
-                <MembershipById<T>>::insert(member_id, membership);
-                Self::deposit_event(RawEvent::MemberSetControllerAccount(member_id, new_controller_account));
+                membership.root_account = root_account;
             }
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn set_root_account(origin, member_id: T::MemberId, new_root_account: T::AccountId) {
-            let sender = ensure_signed(origin)?;
-
-            let mut membership = Self::ensure_membership(member_id)?;
-
-            ensure!(membership.root_account == sender, "only root account can set new root account");
 
-            // only update if new root account is different than current one
-            if membership.root_account != new_root_account {
-                <MemberIdsByRootAccountId<T>>::mutate(&membership.root_account, |ids| {
+            if let Some(controller_account) = new_controller_account {
+                <MemberIdsByControllerAccountId<T>>::mutate(&membership.controller_account, |ids| {
                     ids.retain(|id| *id != member_id);
                 });
 
-                <MemberIdsByRootAccountId<T>>::mutate(&new_root_account, |ids| {
+                <MemberIdsByControllerAccountId<T>>::mutate(&controller_account, |ids| {
                     ids.push(member_id);
                 });
 
-                membership.root_account = new_root_account.clone();
-                <MembershipById<T>>::insert(member_id, membership);
-                Self::deposit_event(RawEvent::MemberSetRootAccount(member_id, new_root_account));
+                membership.controller_account = controller_account;
             }
+
+            <MembershipById<T>>::insert(member_id, membership);
+            Self::deposit_event(RawEvent::MemberAccountsUpdated(member_id));
         }
-    }
-}
 
-/// Reason why a given member id does not have a given account as the controller account.
-pub enum ControllerAccountForMemberCheckFailed {
-    NotMember,
-    NotControllerAccount,
-}
+        /// Updates member profile verification status. Requires working group member origin.
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn update_profile_verification(
+            origin,
+            worker_id: T::ActorId,
+            target_member_id: T::MemberId,
+            is_verified: bool
+        ) {
+            T::WorkingGroup::ensure_worker_origin(origin, &worker_id)?;
 
-pub enum MemberControllerAccountDidNotSign {
-    UnsignedOrigin,
-    MemberIdInvalid,
-    SignerControllerAccountMismatch,
-}
+            Self::ensure_membership(target_member_id)?;
 
-pub enum MemberControllerAccountMismatch {
-    MemberIdInvalid,
-    SignerControllerAccountMismatch,
-}
-pub enum MemberRootAccountMismatch {
-    MemberIdInvalid,
-    SignerRootAccountMismatch,
+            //
+            // == MUTATION SAFE ==
+            //
+
+            <MembershipById<T>>::mutate(&target_member_id, |membership| {
+                    membership.verified = is_verified;
+            });
+
+            Self::deposit_event(
+                RawEvent::MemberVerificationStatusUpdated(target_member_id, is_verified)
+            );
+        }
+
+        /// Updates membership referral cut. Requires root origin.
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn set_referral_cut(
+            origin,
+            value: BalanceOf<T>
+        ) {
+            ensure_root(origin)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            <ReferralCut<T>>::put(value);
+
+            Self::deposit_event(RawEvent::ReferralCutUpdated(value));
+        }
+    }
 }
 
 impl<T: Trait> Module<T> {
     /// Provided that the member_id exists return its membership. Returns error otherwise.
-    pub fn ensure_membership(id: T::MemberId) -> Result<Membership<T>, &'static str> {
+    pub fn ensure_membership(member_id: T::MemberId) -> Result<Membership<T>, Error<T>> {
+        Self::ensure_membership_with_error(member_id, Error::<T>::MemberProfileNotFound)
+    }
+
+    /// Provided that the member_id exists return its membership. Returns provided error otherwise.
+    fn ensure_membership_with_error(
+        id: T::MemberId,
+        error: Error<T>,
+    ) -> Result<Membership<T>, Error<T>> {
         if <MembershipById<T>>::contains_key(&id) {
             Ok(Self::membership(&id))
         } else {
-            Err("member profile not found")
+            Err(error)
         }
     }
 
@@ -382,17 +514,17 @@ impl<T: Trait> Module<T> {
     pub fn ensure_is_controller_account_for_member(
         member_id: &T::MemberId,
         account: &T::AccountId,
-    ) -> Result<Membership<T>, ControllerAccountForMemberCheckFailed> {
+    ) -> Result<Membership<T>, Error<T>> {
         if MembershipById::<T>::contains_key(member_id) {
             let membership = MembershipById::<T>::get(member_id);
 
             if membership.controller_account == *account {
                 Ok(membership)
             } else {
-                Err(ControllerAccountForMemberCheckFailed::NotControllerAccount)
+                Err(Error::<T>::ControllerAccountRequired)
             }
         } else {
-            Err(ControllerAccountForMemberCheckFailed::NotMember)
+            Err(Error::<T>::MemberProfileNotFound)
         }
     }
 
@@ -402,83 +534,97 @@ impl<T: Trait> Module<T> {
             || <MemberIdsByControllerAccountId<T>>::contains_key(who)
     }
 
+    // Ensure possible member handle is unique.
     #[allow(clippy::ptr_arg)] // cannot change to the "&[u8]" suggested by clippy
-    fn ensure_unique_handle(handle: &Vec<u8>) -> DispatchResult {
+    fn ensure_unique_handle(handle: &Vec<u8>) -> Result<(), Error<T>> {
         ensure!(
             !<MemberIdByHandle<T>>::contains_key(handle),
-            "handle already registered"
+            Error::<T>::HandleAlreadyRegistered
         );
         Ok(())
     }
 
-    fn validate_handle(handle: &[u8]) -> DispatchResult {
+    // Provides possible handle validation.
+    fn validate_handle(handle: &[u8]) -> Result<(), Error<T>> {
         ensure!(
             handle.len() >= Self::min_handle_length() as usize,
-            "handle too short"
+            Error::<T>::HandleTooShort
         );
         ensure!(
             handle.len() <= Self::max_handle_length() as usize,
-            "handle too long"
+            Error::<T>::HandleTooLong
         );
         Ok(())
     }
 
+    // Provides possible member about text validation.
     fn validate_text(text: &[u8]) -> Vec<u8> {
         let mut text = text.to_owned();
         text.truncate(Self::max_about_text_length() as usize);
         text
     }
 
-    fn validate_avatar(uri: &[u8]) -> DispatchResult {
+    // Provides possible member avatar uri validation.
+    fn validate_avatar(uri: &[u8]) -> Result<(), Error<T>> {
         ensure!(
             uri.len() <= Self::max_avatar_uri_length() as usize,
-            "avatar uri too long"
+            Error::<T>::AvatarUriTooLong
+        );
+        Ok(())
+    }
+
+    // Provides possible member name validation.
+    fn validate_name(name: &[u8]) -> Result<(), Error<T>> {
+        ensure!(
+            name.len() <= Self::max_name_length() as usize,
+            Error::<T>::NameTooLong
         );
         Ok(())
     }
 
-    /// Basic user input validation
+    // Basic user input validation
     fn check_user_registration_info(
+        name: Option<Vec<u8>>,
         handle: Option<Vec<u8>>,
         avatar_uri: Option<Vec<u8>>,
         about: Option<Vec<u8>>,
-    ) -> Result<ValidatedUserInfo, &'static str> {
+    ) -> Result<ValidatedUserInfo, Error<T>> {
         // Handle is required during registration
-        let handle = handle.ok_or("handle must be provided during registration")?;
+        let handle = handle.ok_or(Error::<T>::HandleMustBeProvidedDuringRegistration)?;
         Self::validate_handle(&handle)?;
 
         let about = Self::validate_text(&about.unwrap_or_default());
         let avatar_uri = avatar_uri.unwrap_or_default();
         Self::validate_avatar(&avatar_uri)?;
+        let name = name.unwrap_or_default();
+        Self::validate_name(&name)?;
 
         Ok(ValidatedUserInfo {
+            name,
             handle,
             avatar_uri,
             about,
         })
     }
 
+    // Inserts a member using a validated information. Sets handle, accounts caches.
     fn insert_member(
         root_account: &T::AccountId,
         controller_account: &T::AccountId,
         user_info: &ValidatedUserInfo,
-        entry_method: EntryMethod,
-        registered_at_block: T::BlockNumber,
-        registered_at_time: T::Moment,
-    ) -> Result<T::MemberId, &'static str> {
+    ) -> Result<T::MemberId, Error<T>> {
         Self::ensure_unique_handle(&user_info.handle)?;
 
         let new_member_id = Self::members_created();
 
         let membership: Membership<T> = MembershipObject {
+            name: user_info.name.clone(),
             handle: user_info.handle.clone(),
             avatar_uri: user_info.avatar_uri.clone(),
             about: user_info.about.clone(),
-            registered_at_block,
-            registered_at_time,
-            entry: entry_method,
             root_account: root_account.clone(),
             controller_account: controller_account.clone(),
+            verified: false,
         };
 
         <MemberIdsByRootAccountId<T>>::mutate(root_account, |ids| {
@@ -495,85 +641,46 @@ impl<T: Trait> Module<T> {
         Ok(new_member_id)
     }
 
-    fn _change_member_about_text(id: T::MemberId, text: &[u8]) -> DispatchResult {
-        let mut membership = Self::ensure_membership(id)?;
-        let text = Self::validate_text(text);
-        membership.about = text;
-        Self::deposit_event(RawEvent::MemberUpdatedAboutText(id));
-        <MembershipById<T>>::insert(id, membership);
-        Ok(())
-    }
-
-    fn _change_member_avatar(id: T::MemberId, uri: &[u8]) -> DispatchResult {
-        let mut membership = Self::ensure_membership(id)?;
-        Self::validate_avatar(uri)?;
-        membership.avatar_uri = uri.to_owned();
-        Self::deposit_event(RawEvent::MemberUpdatedAvatar(id));
-        <MembershipById<T>>::insert(id, membership);
-        Ok(())
-    }
-
-    fn _change_member_handle(id: T::MemberId, handle: Vec<u8>) -> DispatchResult {
-        let mut membership = Self::ensure_membership(id)?;
-        Self::validate_handle(&handle)?;
-        Self::ensure_unique_handle(&handle)?;
-        <MemberIdByHandle<T>>::remove(&membership.handle);
-        <MemberIdByHandle<T>>::insert(handle.clone(), id);
-        membership.handle = handle;
-        Self::deposit_event(RawEvent::MemberUpdatedHandle(id));
-        <MembershipById<T>>::insert(id, membership);
-        Ok(())
-    }
-
+    /// Ensure origin corresponds to the controller account of the member.
     pub fn ensure_member_controller_account_signed(
         origin: T::Origin,
         member_id: &T::MemberId,
-    ) -> Result<T::AccountId, MemberControllerAccountDidNotSign> {
+    ) -> Result<T::AccountId, Error<T>> {
         // Ensure transaction is signed.
-        let signer_account =
-            ensure_signed(origin).map_err(|_| MemberControllerAccountDidNotSign::UnsignedOrigin)?;
+        let signer_account = ensure_signed(origin).map_err(|_| Error::<T>::UnsignedOrigin)?;
 
         // Ensure member exists
-        let membership = Self::ensure_membership(*member_id)
-            .map_err(|_| MemberControllerAccountDidNotSign::MemberIdInvalid)?;
+        let membership = Self::ensure_membership(*member_id)?;
 
         ensure!(
             membership.controller_account == signer_account,
-            MemberControllerAccountDidNotSign::SignerControllerAccountMismatch
+            Error::<T>::ControllerAccountRequired
         );
 
         Ok(signer_account)
     }
 
+    /// Validates that a member has the controller account.
     pub fn ensure_member_controller_account(
         signer_account: &T::AccountId,
         member_id: &T::MemberId,
-    ) -> Result<(), MemberControllerAccountMismatch> {
+    ) -> Result<(), Error<T>> {
         // Ensure member exists
-        let membership = Self::ensure_membership(*member_id)
-            .map_err(|_| MemberControllerAccountMismatch::MemberIdInvalid)?;
+        let membership = Self::ensure_membership(*member_id)?;
 
         ensure!(
             membership.controller_account == *signer_account,
-            MemberControllerAccountMismatch::SignerControllerAccountMismatch
+            Error::<T>::ControllerAccountRequired
         );
 
         Ok(())
     }
 
-    pub fn ensure_member_root_account(
-        signer_account: &T::AccountId,
-        member_id: &T::MemberId,
-    ) -> Result<(), MemberRootAccountMismatch> {
-        // Ensure member exists
-        let membership = Self::ensure_membership(*member_id)
-            .map_err(|_| MemberRootAccountMismatch::MemberIdInvalid)?;
-
-        ensure!(
-            membership.root_account == *signer_account,
-            MemberRootAccountMismatch::SignerRootAccountMismatch
-        );
+    // Calculate current referral bonus. It minimum between membership fee and referral cut.
+    pub(crate) fn get_referral_bonus() -> BalanceOf<T> {
+        let membership_fee = T::MembershipFee::get();
+        let referral_cut = Self::referral_cut();
 
-        Ok(())
+        membership_fee.min(referral_cut)
     }
 }

+ 0 - 132
runtime-modules/membership/src/mock.rs

@@ -1,132 +0,0 @@
-#![cfg(test)]
-
-pub use crate::{GenesisConfig, Trait};
-
-pub use frame_support::traits::Currency;
-use frame_support::{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;
-
-impl_outer_origin! {
-    pub enum Origin for Test {}
-}
-
-// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct Test;
-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 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 = ();
-    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 = ();
-}
-
-parameter_types! {
-    pub const ExistentialDeposit: u32 = 0;
-    pub const MembershipFee: u64 = 100;
-}
-
-impl balances::Trait for Test {
-    type Balance = u64;
-    type DustRemoval = ();
-    type Event = ();
-    type ExistentialDeposit = ExistentialDeposit;
-    type AccountStore = System;
-    type WeightInfo = ();
-    type MaxLocks = ();
-}
-
-impl GovernanceCurrency for Test {
-    type Currency = balances::Module<Self>;
-}
-
-impl Trait for Test {
-    type Event = ();
-    type MemberId = u64;
-    type ActorId = u32;
-    type MembershipFee = MembershipFee;
-}
-
-pub struct TestExternalitiesBuilder<T: Trait> {
-    system_config: Option<frame_system::GenesisConfig>,
-    membership_config: Option<GenesisConfig<T>>,
-}
-
-impl<T: Trait> Default for TestExternalitiesBuilder<T> {
-    fn default() -> Self {
-        Self {
-            system_config: None,
-            membership_config: None,
-        }
-    }
-}
-
-impl<T: Trait> TestExternalitiesBuilder<T> {
-    pub fn set_membership_config(mut self, membership_config: GenesisConfig<T>) -> Self {
-        self.membership_config = Some(membership_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(GenesisConfig::default())
-            .assimilate_storage(&mut t)
-            .unwrap();
-
-        t.into()
-    }
-}
-
-pub type Balances = balances::Module<Test>;
-pub type Members = crate::Module<Test>;
-pub type System = frame_system::Module<Test>;

+ 0 - 272
runtime-modules/membership/src/tests.rs

@@ -1,272 +0,0 @@
-#![cfg(test)]
-
-use super::genesis;
-use super::mock::*;
-
-use frame_support::traits::{LockIdentifier, LockableCurrency, WithdrawReasons};
-use frame_support::*;
-
-fn get_membership_by_id(member_id: u64) -> crate::Membership<Test> {
-    if <crate::MembershipById<Test>>::contains_key(member_id) {
-        Members::membership(member_id)
-    } else {
-        panic!("member profile not created");
-    }
-}
-
-fn assert_dispatch_error_message(result: Result<(), &'static str>, expected_message: &'static str) {
-    assert!(result.is_err());
-    let message = result.err().unwrap();
-    assert_eq!(message, expected_message);
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct TestUserInfo {
-    pub handle: Option<Vec<u8>>,
-    pub avatar_uri: Option<Vec<u8>>,
-    pub about: Option<Vec<u8>>,
-}
-
-fn get_alice_info() -> TestUserInfo {
-    TestUserInfo {
-        handle: Some(String::from("alice").as_bytes().to_vec()),
-        avatar_uri: Some(
-            String::from("http://avatar-url.com/alice")
-                .as_bytes()
-                .to_vec(),
-        ),
-        about: Some(String::from("my name is alice").as_bytes().to_vec()),
-    }
-}
-
-fn get_bob_info() -> TestUserInfo {
-    TestUserInfo {
-        handle: Some(String::from("bobby").as_bytes().to_vec()),
-        avatar_uri: Some(
-            String::from("http://avatar-url.com/bob")
-                .as_bytes()
-                .to_vec(),
-        ),
-        about: Some(String::from("my name is bob").as_bytes().to_vec()),
-    }
-}
-
-const ALICE_ACCOUNT_ID: u64 = 1;
-
-fn buy_default_membership_as_alice() -> crate::DispatchResult {
-    let info = get_alice_info();
-    Members::buy_membership(
-        Origin::signed(ALICE_ACCOUNT_ID),
-        info.handle,
-        info.avatar_uri,
-        info.about,
-    )
-    .map_err(|err| err.into())
-}
-
-fn set_alice_free_balance(balance: u64) {
-    let _ = Balances::deposit_creating(&ALICE_ACCOUNT_ID, balance);
-}
-
-#[test]
-fn buy_membership() {
-    const SURPLUS_BALANCE: u64 = 500;
-
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(genesis::GenesisConfigBuilder::default().build())
-        .build()
-        .execute_with(|| {
-            let initial_balance = MembershipFee::get() + SURPLUS_BALANCE;
-            set_alice_free_balance(initial_balance);
-
-            let next_member_id = Members::members_created();
-
-            assert_ok!(buy_default_membership_as_alice());
-
-            let member_ids = vec![0];
-            assert_eq!(member_ids, vec![next_member_id]);
-
-            let profile = get_membership_by_id(next_member_id);
-
-            assert_eq!(Some(profile.handle), get_alice_info().handle);
-            assert_eq!(Some(profile.avatar_uri), get_alice_info().avatar_uri);
-            assert_eq!(Some(profile.about), get_alice_info().about);
-
-            assert_eq!(Balances::free_balance(&ALICE_ACCOUNT_ID), SURPLUS_BALANCE);
-
-            // controller account initially set to primary account
-            assert_eq!(profile.controller_account, ALICE_ACCOUNT_ID);
-            assert_eq!(
-                <crate::MemberIdsByControllerAccountId<Test>>::get(ALICE_ACCOUNT_ID),
-                vec![next_member_id]
-            );
-        });
-}
-
-#[test]
-fn buy_membership_fails_without_enough_balance() {
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(genesis::GenesisConfigBuilder::default().build())
-        .build()
-        .execute_with(|| {
-            let initial_balance = MembershipFee::get() - 1;
-            set_alice_free_balance(initial_balance);
-
-            assert_dispatch_error_message(
-                buy_default_membership_as_alice(),
-                "not enough balance to buy membership",
-            );
-        });
-}
-
-#[test]
-fn buy_membership_fails_without_enough_balance_with_locked_balance() {
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(genesis::GenesisConfigBuilder::default().build())
-        .build()
-        .execute_with(|| {
-            let initial_balance = MembershipFee::get();
-            let lock_id = LockIdentifier::default();
-            Balances::set_lock(lock_id, &ALICE_ACCOUNT_ID, 1, WithdrawReasons::all());
-            set_alice_free_balance(initial_balance);
-
-            assert_dispatch_error_message(
-                buy_default_membership_as_alice(),
-                "not enough balance to buy membership",
-            );
-        });
-}
-
-#[test]
-fn new_memberships_allowed_flag() {
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(genesis::GenesisConfigBuilder::default().build())
-        .build()
-        .execute_with(|| {
-            let initial_balance = MembershipFee::get() + 1;
-            set_alice_free_balance(initial_balance);
-
-            crate::NewMembershipsAllowed::put(false);
-
-            assert_dispatch_error_message(
-                buy_default_membership_as_alice(),
-                "new members not allowed",
-            );
-        });
-}
-
-#[test]
-fn unique_handles() {
-    const SURPLUS_BALANCE: u64 = 500;
-
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(genesis::GenesisConfigBuilder::default().build())
-        .build()
-        .execute_with(|| {
-            let initial_balance = MembershipFee::get() + SURPLUS_BALANCE;
-            set_alice_free_balance(initial_balance);
-
-            // alice's handle already taken
-            <crate::MemberIdByHandle<Test>>::insert(get_alice_info().handle.unwrap(), 1);
-
-            // should not be allowed to buy membership with that handle
-            assert_dispatch_error_message(
-                buy_default_membership_as_alice(),
-                "handle already registered",
-            );
-        });
-}
-
-#[test]
-fn update_profile() {
-    const SURPLUS_BALANCE: u64 = 500;
-
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(genesis::GenesisConfigBuilder::default().build())
-        .build()
-        .execute_with(|| {
-            let initial_balance = MembershipFee::get() + SURPLUS_BALANCE;
-            set_alice_free_balance(initial_balance);
-
-            let next_member_id = Members::members_created();
-
-            assert_ok!(buy_default_membership_as_alice());
-            let info = get_bob_info();
-            assert_ok!(Members::update_membership(
-                Origin::signed(ALICE_ACCOUNT_ID),
-                next_member_id,
-                info.handle,
-                info.avatar_uri,
-                info.about,
-            ));
-
-            let profile = get_membership_by_id(next_member_id);
-
-            assert_eq!(Some(profile.handle), get_bob_info().handle);
-            assert_eq!(Some(profile.avatar_uri), get_bob_info().avatar_uri);
-            assert_eq!(Some(profile.about), get_bob_info().about);
-        });
-}
-
-#[test]
-fn set_controller_key() {
-    let initial_members = [(0, ALICE_ACCOUNT_ID)];
-    const ALICE_CONTROLLER_ID: u64 = 2;
-
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(
-            genesis::GenesisConfigBuilder::default()
-                .members(initial_members.to_vec())
-                .build(),
-        )
-        .build()
-        .execute_with(|| {
-            let member_id = 0;
-
-            assert_ok!(Members::set_controller_account(
-                Origin::signed(ALICE_ACCOUNT_ID),
-                member_id,
-                ALICE_CONTROLLER_ID
-            ));
-
-            let profile = get_membership_by_id(member_id);
-
-            assert_eq!(profile.controller_account, ALICE_CONTROLLER_ID);
-            assert_eq!(
-                <crate::MemberIdsByControllerAccountId<Test>>::get(&ALICE_CONTROLLER_ID),
-                vec![member_id]
-            );
-            assert!(
-                <crate::MemberIdsByControllerAccountId<Test>>::get(&ALICE_ACCOUNT_ID).is_empty()
-            );
-        });
-}
-
-#[test]
-fn set_root_account() {
-    let initial_members = [(0, ALICE_ACCOUNT_ID)];
-    const ALICE_NEW_ROOT_ACCOUNT: u64 = 2;
-
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(
-            genesis::GenesisConfigBuilder::default()
-                .members(initial_members.to_vec())
-                .build(),
-        )
-        .build()
-        .execute_with(|| {
-            let member_id = 0;
-
-            assert_ok!(Members::set_root_account(
-                Origin::signed(ALICE_ACCOUNT_ID),
-                member_id,
-                ALICE_NEW_ROOT_ACCOUNT
-            ));
-
-            let membership = Members::membership(member_id);
-
-            assert_eq!(ALICE_NEW_ROOT_ACCOUNT, membership.root_account);
-
-            assert!(<crate::MemberIdsByRootAccountId<Test>>::get(&ALICE_ACCOUNT_ID).is_empty());
-        });
-}

+ 265 - 0
runtime-modules/membership/src/tests/fixtures.rs

@@ -0,0 +1,265 @@
+use super::mock::*;
+use crate::BuyMembershipParameters;
+use frame_support::dispatch::DispatchResult;
+use frame_support::traits::{OnFinalize, OnInitialize};
+use frame_support::StorageMap;
+use frame_system::{EventRecord, Phase, RawOrigin};
+
+// 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());
+        <Membership 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());
+        <Membership as OnInitialize<u64>>::on_initialize(System::block_number());
+    }
+}
+
+pub struct EventFixture;
+impl EventFixture {
+    pub fn assert_last_crate_event(expected_raw_event: crate::Event<Test>) {
+        let converted_event = TestEvent::membership_mod(expected_raw_event);
+
+        Self::assert_last_global_event(converted_event)
+    }
+
+    pub fn assert_last_global_event(expected_event: TestEvent) {
+        let expected_event = EventRecord {
+            phase: Phase::Initialization,
+            event: expected_event,
+            topics: vec![],
+        };
+
+        assert_eq!(System::events().pop().unwrap(), expected_event);
+    }
+}
+
+pub fn get_membership_by_id(member_id: u64) -> crate::Membership<Test> {
+    if <crate::MembershipById<Test>>::contains_key(member_id) {
+        Membership::membership(member_id)
+    } else {
+        panic!("member profile not created");
+    }
+}
+
+pub fn assert_dispatch_error_message(result: DispatchResult, expected_result: DispatchResult) {
+    assert!(result.is_err());
+    assert_eq!(result, expected_result);
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct TestUserInfo {
+    pub name: Option<Vec<u8>>,
+    pub handle: Option<Vec<u8>>,
+    pub avatar_uri: Option<Vec<u8>>,
+    pub about: Option<Vec<u8>>,
+}
+
+pub fn get_alice_info() -> TestUserInfo {
+    TestUserInfo {
+        name: Some(b"Alice".to_vec()),
+        handle: Some(b"alice".to_vec()),
+        avatar_uri: Some(b"http://avatar-url.com/alice".to_vec()),
+        about: Some(b"my name is alice".to_vec()),
+    }
+}
+
+pub fn get_bob_info() -> TestUserInfo {
+    TestUserInfo {
+        name: Some(b"Bob".to_vec()),
+        handle: Some(b"bobby".to_vec()),
+        avatar_uri: Some(b"http://avatar-url.com/bob".to_vec()),
+        about: Some(b"my name is bob".to_vec()),
+    }
+}
+
+pub const ALICE_ACCOUNT_ID: u64 = 1;
+pub const BOB_ACCOUNT_ID: u64 = 2;
+
+pub fn buy_default_membership_as_alice() -> DispatchResult {
+    let info = get_alice_info();
+
+    let params = BuyMembershipParameters {
+        root_account: ALICE_ACCOUNT_ID,
+        controller_account: ALICE_ACCOUNT_ID,
+        name: info.name,
+        handle: info.handle,
+        avatar_uri: info.avatar_uri,
+        about: info.about,
+        referrer_id: None,
+    };
+
+    Membership::buy_membership(Origin::signed(ALICE_ACCOUNT_ID), params)
+}
+
+pub fn set_alice_free_balance(balance: u64) {
+    let _ = Balances::deposit_creating(&ALICE_ACCOUNT_ID, balance);
+}
+
+pub struct UpdateMembershipVerificationFixture {
+    pub origin: RawOrigin<u64>,
+    pub worker_id: u64,
+    pub member_id: u64,
+    pub verified: bool,
+}
+
+impl Default for UpdateMembershipVerificationFixture {
+    fn default() -> Self {
+        Self {
+            origin: RawOrigin::Signed(ALICE_ACCOUNT_ID),
+            worker_id: 1,
+            member_id: 1,
+            verified: true,
+        }
+    }
+}
+
+impl UpdateMembershipVerificationFixture {
+    pub fn call_and_assert(&self, expected_result: DispatchResult) {
+        let actual_result = Membership::update_profile_verification(
+            self.origin.clone().into(),
+            self.worker_id,
+            self.member_id,
+            self.verified,
+        );
+
+        assert_eq!(expected_result, actual_result);
+
+        if actual_result.is_ok() {
+            let membership = get_membership_by_id(self.member_id);
+            assert_eq!(membership.verified, self.verified);
+        }
+    }
+
+    pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
+        Self { origin, ..self }
+    }
+
+    pub fn with_member_id(self, member_id: u64) -> Self {
+        Self { member_id, ..self }
+    }
+
+    pub fn with_worker_id(self, worker_id: u64) -> Self {
+        Self { worker_id, ..self }
+    }
+}
+
+pub struct BuyMembershipFixture {
+    pub origin: RawOrigin<u64>,
+    pub root_account: u64,
+    pub controller_account: u64,
+    pub name: Option<Vec<u8>>,
+    pub handle: Option<Vec<u8>>,
+    pub avatar_uri: Option<Vec<u8>>,
+    pub about: Option<Vec<u8>>,
+    pub referrer_id: Option<u64>,
+}
+
+impl Default for BuyMembershipFixture {
+    fn default() -> Self {
+        let alice = get_alice_info();
+        Self {
+            origin: RawOrigin::Signed(ALICE_ACCOUNT_ID),
+            root_account: ALICE_ACCOUNT_ID,
+            controller_account: ALICE_ACCOUNT_ID,
+            name: alice.name,
+            handle: alice.handle,
+            avatar_uri: alice.avatar_uri,
+            about: alice.about,
+            referrer_id: None,
+        }
+    }
+}
+
+impl BuyMembershipFixture {
+    pub fn call_and_assert(&self, expected_result: DispatchResult) {
+        let params = BuyMembershipParameters {
+            root_account: self.root_account.clone(),
+            controller_account: self.controller_account.clone(),
+            name: self.name.clone(),
+            handle: self.handle.clone(),
+            avatar_uri: self.avatar_uri.clone(),
+            about: self.about.clone(),
+            referrer_id: self.referrer_id.clone(),
+        };
+
+        let actual_result = Membership::buy_membership(self.origin.clone().into(), params);
+
+        assert_eq!(expected_result, actual_result);
+    }
+
+    pub fn with_referrer_id(self, referrer_id: u64) -> Self {
+        Self {
+            referrer_id: Some(referrer_id),
+            ..self
+        }
+    }
+
+    pub fn with_name(self, name: Vec<u8>) -> Self {
+        Self {
+            name: Some(name),
+            ..self
+        }
+    }
+
+    pub fn with_handle(self, handle: Vec<u8>) -> Self {
+        Self {
+            handle: Some(handle),
+            ..self
+        }
+    }
+
+    pub fn with_accounts(self, account_id: u64) -> Self {
+        Self {
+            root_account: account_id,
+            controller_account: account_id,
+            ..self
+        }
+    }
+
+    pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
+        Self { origin, ..self }
+    }
+}
+
+pub(crate) fn increase_total_balance_issuance_using_account_id(account_id: u64, balance: u64) {
+    let initial_balance = Balances::total_issuance();
+    {
+        let _ = Balances::deposit_creating(&account_id, balance);
+    }
+    assert_eq!(Balances::total_issuance(), initial_balance + balance);
+}
+
+pub struct SetReferralCutFixture {
+    pub origin: RawOrigin<u64>,
+    pub value: u64,
+}
+
+pub const DEFAULT_REFERRAL_CUT_VALUE: u64 = 100;
+
+impl Default for SetReferralCutFixture {
+    fn default() -> Self {
+        Self {
+            origin: RawOrigin::Root,
+            value: DEFAULT_REFERRAL_CUT_VALUE,
+        }
+    }
+}
+
+impl SetReferralCutFixture {
+    pub fn call_and_assert(&self, expected_result: DispatchResult) {
+        let actual_result = Membership::set_referral_cut(self.origin.clone().into(), self.value);
+
+        assert_eq!(expected_result, actual_result);
+
+        if actual_result.is_ok() {
+            assert_eq!(Membership::referral_cut(), self.value);
+        }
+    }
+
+    pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
+        Self { origin, ..self }
+    }
+}

+ 313 - 0
runtime-modules/membership/src/tests/mock.rs

@@ -0,0 +1,313 @@
+#![cfg(test)]
+
+pub use crate::{GenesisConfig, Trait};
+
+use staking_handler::LockComparator;
+
+pub use frame_support::traits::{Currency, LockIdentifier};
+use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
+
+pub use frame_system;
+use frame_system::RawOrigin;
+use sp_core::H256;
+use sp_runtime::{
+    testing::Header,
+    traits::{BlakeTwo256, IdentityLookup},
+    DispatchError, DispatchResult, Perbill,
+};
+
+pub(crate) type MembershipWorkingGroupInstance = working_group::Instance4;
+
+impl_outer_origin! {
+    pub enum Origin for Test {}
+}
+
+mod membership_mod {
+    pub use crate::Event;
+}
+
+impl_outer_event! {
+    pub enum TestEvent for Test {
+        membership_mod<T>,
+        frame_system<T>,
+        balances<T>,
+        working_group Instance4 <T>,
+    }
+}
+
+// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct Test;
+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 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 = ();
+}
+
+parameter_types! {
+    pub const ExistentialDeposit: u32 = 0;
+    pub const MembershipFee: u64 = 100;
+}
+
+impl balances::Trait for Test {
+    type Balance = u64;
+    type DustRemoval = ();
+    type Event = TestEvent;
+    type ExistentialDeposit = ExistentialDeposit;
+    type AccountStore = System;
+    type WeightInfo = ();
+    type MaxLocks = ();
+}
+
+impl common::Trait for Test {
+    type MemberId = u64;
+    type ActorId = u64;
+}
+
+parameter_types! {
+    pub const MaxWorkerNumberLimit: u32 = 3;
+    pub const LockId: LockIdentifier = [9; 8];
+}
+
+impl working_group::Trait<MembershipWorkingGroupInstance> for Test {
+    type Event = TestEvent;
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, LockId>;
+    type MemberOriginValidator = ();
+    type MinUnstakingPeriodLimit = ();
+    type RewardPeriod = ();
+    type WeightInfo = Weights;
+}
+
+impl LockComparator<u64> for Test {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[LockIdentifier],
+    ) -> bool {
+        false
+    }
+}
+
+// Weights info stub
+pub struct Weights;
+impl working_group::WeightInfo for Weights {
+    fn on_initialize_leaving(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn on_initialize_rewarding_with_missing_reward(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn on_initialize_rewarding_with_missing_reward_cant_pay(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn on_initialize_rewarding_without_missing_reward(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn apply_on_opening(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn fill_opening_lead() -> u64 {
+        unimplemented!()
+    }
+
+    fn fill_opening_worker(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn update_role_account() -> u64 {
+        unimplemented!()
+    }
+
+    fn cancel_opening() -> u64 {
+        unimplemented!()
+    }
+
+    fn withdraw_application() -> u64 {
+        unimplemented!()
+    }
+
+    fn slash_stake(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn terminate_role_worker(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn terminate_role_lead(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn increase_stake() -> u64 {
+        unimplemented!()
+    }
+
+    fn decrease_stake() -> u64 {
+        unimplemented!()
+    }
+
+    fn spend_from_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn update_reward_amount() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_status_text(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn update_reward_account() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn add_opening(_: u32) -> u64 {
+        unimplemented!()
+    }
+
+    fn leave_role_immediatly() -> u64 {
+        unimplemented!()
+    }
+
+    fn leave_role_later() -> u64 {
+        unimplemented!()
+    }
+}
+
+impl common::origin::ActorOriginValidator<Origin, u64, u64> for () {
+    fn ensure_actor_origin(origin: Origin, _: u64) -> Result<u64, &'static str> {
+        let account_id = frame_system::ensure_signed(origin)?;
+
+        Ok(account_id)
+    }
+}
+
+impl Trait for Test {
+    type Event = TestEvent;
+    type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
+}
+
+impl common::working_group::WorkingGroupIntegration<Test> for () {
+    fn ensure_worker_origin(
+        origin: <Test as frame_system::Trait>::Origin,
+        worker_id: &<Test as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        let raw_origin: Result<RawOrigin<u64>, <Test as frame_system::Trait>::Origin> =
+            origin.into();
+
+        if let RawOrigin::Signed(_) = raw_origin.unwrap() {
+            if *worker_id == 1 {
+                Ok(())
+            } else {
+                Err(working_group::Error::<Test, MembershipWorkingGroupInstance>::WorkerDoesNotExist.into())
+            }
+        } else {
+            Err(DispatchError::BadOrigin)
+        }
+    }
+}
+
+pub struct TestExternalitiesBuilder<T: Trait> {
+    system_config: Option<frame_system::GenesisConfig>,
+    membership_config: Option<GenesisConfig<T>>,
+}
+
+impl<T: Trait> Default for TestExternalitiesBuilder<T> {
+    fn default() -> Self {
+        Self {
+            system_config: None,
+            membership_config: None,
+        }
+    }
+}
+
+impl<T: Trait> TestExternalitiesBuilder<T> {
+    pub fn set_membership_config(mut self, membership_config: GenesisConfig<T>) -> Self {
+        self.membership_config = Some(membership_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(GenesisConfig::default())
+            .assimilate_storage(&mut t)
+            .unwrap();
+
+        t.into()
+    }
+}
+
+pub fn build_test_externalities() -> sp_io::TestExternalities {
+    TestExternalitiesBuilder::<Test>::default().build()
+}
+
+pub fn build_test_externalities_with_initial_members(
+    initial_members: Vec<(u64, u64)>,
+) -> sp_io::TestExternalities {
+    TestExternalitiesBuilder::<Test>::default()
+        .set_membership_config(
+            crate::genesis::GenesisConfigBuilder::default()
+                .members(initial_members)
+                .build(),
+        )
+        .build()
+}
+
+pub type Balances = balances::Module<Test>;
+pub type Membership = crate::Module<Test>;
+pub type System = frame_system::Module<Test>;

+ 406 - 0
runtime-modules/membership/src/tests/mod.rs

@@ -0,0 +1,406 @@
+#![cfg(test)]
+
+pub(crate) mod fixtures;
+pub(crate) mod mock;
+
+use crate::{Error, Event};
+use fixtures::*;
+use mock::*;
+
+use frame_support::traits::{LockIdentifier, LockableCurrency, WithdrawReasons};
+use frame_support::{assert_ok, StorageMap, StorageValue};
+use frame_system::RawOrigin;
+use sp_runtime::DispatchError;
+
+#[test]
+fn buy_membership_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let starting_block = 1;
+        run_to_block(starting_block);
+
+        let initial_balance = MembershipFee::get();
+        set_alice_free_balance(initial_balance);
+
+        let next_member_id = Membership::members_created();
+
+        assert_ok!(buy_default_membership_as_alice());
+
+        let member_ids = vec![0];
+        assert_eq!(member_ids, vec![next_member_id]);
+
+        let profile = get_membership_by_id(next_member_id);
+
+        assert_eq!(Some(profile.name), get_alice_info().name);
+        assert_eq!(Some(profile.handle), get_alice_info().handle);
+        assert_eq!(Some(profile.avatar_uri), get_alice_info().avatar_uri);
+        assert_eq!(Some(profile.about), get_alice_info().about);
+
+        // controller account initially set to primary account
+        assert_eq!(profile.controller_account, ALICE_ACCOUNT_ID);
+        assert_eq!(
+            <crate::MemberIdsByControllerAccountId<Test>>::get(ALICE_ACCOUNT_ID),
+            vec![next_member_id]
+        );
+
+        EventFixture::assert_last_crate_event(Event::<Test>::MemberRegistered(next_member_id));
+    });
+}
+
+#[test]
+fn buy_membership_fails_without_enough_balance() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = MembershipFee::get() - 1;
+        set_alice_free_balance(initial_balance);
+
+        assert_dispatch_error_message(
+            buy_default_membership_as_alice(),
+            Err(Error::<Test>::NotEnoughBalanceToBuyMembership.into()),
+        );
+    });
+}
+
+#[test]
+fn buy_membership_fails_without_enough_balance_with_locked_balance() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = MembershipFee::get();
+        let lock_id = LockIdentifier::default();
+        Balances::set_lock(lock_id, &ALICE_ACCOUNT_ID, 1, WithdrawReasons::all());
+        set_alice_free_balance(initial_balance);
+
+        assert_dispatch_error_message(
+            buy_default_membership_as_alice(),
+            Err(Error::<Test>::NotEnoughBalanceToBuyMembership.into()),
+        );
+    });
+}
+
+#[test]
+fn new_memberships_allowed_flag_works() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = MembershipFee::get() + 1;
+        set_alice_free_balance(initial_balance);
+
+        crate::NewMembershipsAllowed::put(false);
+
+        assert_dispatch_error_message(
+            buy_default_membership_as_alice(),
+            Err(Error::<Test>::NewMembersNotAllowed.into()),
+        );
+    });
+}
+
+#[test]
+fn buy_membership_fails_with_non_unique_handle() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = MembershipFee::get();
+        set_alice_free_balance(initial_balance);
+
+        // alice's handle already taken
+        <crate::MemberIdByHandle<Test>>::insert(get_alice_info().handle.unwrap(), 1);
+
+        // should not be allowed to buy membership with that handle
+        assert_dispatch_error_message(
+            buy_default_membership_as_alice(),
+            Err(Error::<Test>::HandleAlreadyRegistered.into()),
+        );
+    });
+}
+
+#[test]
+fn update_profile_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let starting_block = 1;
+        run_to_block(starting_block);
+
+        let initial_balance = MembershipFee::get();
+        set_alice_free_balance(initial_balance);
+
+        let next_member_id = Membership::members_created();
+
+        assert_ok!(buy_default_membership_as_alice());
+        let info = get_bob_info();
+        assert_ok!(Membership::update_profile(
+            Origin::signed(ALICE_ACCOUNT_ID),
+            next_member_id,
+            info.name,
+            info.handle,
+            info.avatar_uri,
+            info.about,
+        ));
+
+        let profile = get_membership_by_id(next_member_id);
+
+        assert_eq!(Some(profile.name), get_bob_info().name);
+        assert_eq!(Some(profile.handle), get_bob_info().handle);
+        assert_eq!(Some(profile.avatar_uri), get_bob_info().avatar_uri);
+        assert_eq!(Some(profile.about), get_bob_info().about);
+
+        assert!(<crate::MemberIdByHandle<Test>>::contains_key(
+            get_bob_info().handle.unwrap()
+        ));
+
+        EventFixture::assert_last_crate_event(Event::<Test>::MemberProfileUpdated(next_member_id));
+    });
+}
+
+#[test]
+fn update_profile_has_no_effect_on_empty_parameters() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = MembershipFee::get();
+        set_alice_free_balance(initial_balance);
+
+        let next_member_id = Membership::members_created();
+
+        assert_ok!(buy_default_membership_as_alice());
+        assert_ok!(Membership::update_profile(
+            Origin::signed(ALICE_ACCOUNT_ID),
+            next_member_id,
+            None,
+            None,
+            None,
+            None,
+        ));
+
+        let profile = get_membership_by_id(next_member_id);
+
+        assert_eq!(Some(profile.name), get_alice_info().name);
+        assert_eq!(Some(profile.handle), get_alice_info().handle);
+        assert_eq!(Some(profile.avatar_uri), get_alice_info().avatar_uri);
+        assert_eq!(Some(profile.about), get_alice_info().about);
+
+        assert!(<crate::MemberIdByHandle<Test>>::contains_key(
+            get_alice_info().handle.unwrap()
+        ));
+    });
+}
+
+#[test]
+fn update_profile_accounts_succeeds() {
+    let member_id = 0u64;
+    let initial_members = [(member_id, ALICE_ACCOUNT_ID)];
+
+    build_test_externalities_with_initial_members(initial_members.to_vec()).execute_with(|| {
+        let starting_block = 1;
+        run_to_block(starting_block);
+
+        const ALICE_NEW_ACCOUNT_ID: u64 = 2;
+
+        assert_ok!(Membership::update_accounts(
+            Origin::signed(ALICE_ACCOUNT_ID),
+            member_id,
+            Some(ALICE_NEW_ACCOUNT_ID),
+            Some(ALICE_NEW_ACCOUNT_ID),
+        ));
+
+        let profile = get_membership_by_id(member_id);
+
+        assert_eq!(profile.controller_account, ALICE_NEW_ACCOUNT_ID);
+        assert_eq!(
+            <crate::MemberIdsByControllerAccountId<Test>>::get(&ALICE_NEW_ACCOUNT_ID),
+            vec![member_id]
+        );
+        assert!(<crate::MemberIdsByControllerAccountId<Test>>::get(&ALICE_ACCOUNT_ID).is_empty());
+
+        assert_eq!(profile.root_account, ALICE_NEW_ACCOUNT_ID);
+        assert_eq!(
+            <crate::MemberIdsByRootAccountId<Test>>::get(&ALICE_NEW_ACCOUNT_ID),
+            vec![member_id]
+        );
+        assert!(<crate::MemberIdsByRootAccountId<Test>>::get(&ALICE_ACCOUNT_ID).is_empty());
+
+        EventFixture::assert_last_crate_event(Event::<Test>::MemberAccountsUpdated(member_id));
+    });
+}
+
+#[test]
+fn update_accounts_has_effect_on_empty_account_parameters() {
+    let member_id = 0u64;
+    let initial_members = [(member_id, ALICE_ACCOUNT_ID)];
+
+    build_test_externalities_with_initial_members(initial_members.to_vec()).execute_with(|| {
+        assert_ok!(Membership::update_accounts(
+            Origin::signed(ALICE_ACCOUNT_ID),
+            member_id,
+            None,
+            None,
+        ));
+
+        let profile = get_membership_by_id(member_id);
+
+        assert_eq!(profile.controller_account, ALICE_ACCOUNT_ID);
+        assert_eq!(
+            <crate::MemberIdsByControllerAccountId<Test>>::get(&ALICE_ACCOUNT_ID),
+            vec![member_id]
+        );
+
+        assert_eq!(profile.root_account, ALICE_ACCOUNT_ID);
+        assert_eq!(
+            <crate::MemberIdsByRootAccountId<Test>>::get(&ALICE_ACCOUNT_ID),
+            vec![member_id]
+        );
+    });
+}
+
+#[test]
+fn update_verification_status_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let starting_block = 1;
+        run_to_block(starting_block);
+
+        let initial_balance = MembershipFee::get();
+        set_alice_free_balance(initial_balance);
+
+        let next_member_id = Membership::members_created();
+        assert_ok!(buy_default_membership_as_alice());
+
+        UpdateMembershipVerificationFixture::default()
+            .with_member_id(next_member_id)
+            .call_and_assert(Ok(()));
+
+        EventFixture::assert_last_crate_event(Event::<Test>::MemberVerificationStatusUpdated(
+            next_member_id,
+            true,
+        ));
+    });
+}
+
+#[test]
+fn update_verification_status_fails_with_bad_origin() {
+    build_test_externalities().execute_with(|| {
+        let next_member_id = Membership::members_created();
+
+        UpdateMembershipVerificationFixture::default()
+            .with_member_id(next_member_id)
+            .with_origin(RawOrigin::None)
+            .call_and_assert(Err(DispatchError::BadOrigin));
+    });
+}
+
+#[test]
+fn update_verification_status_fails_with_invalid_member_id() {
+    build_test_externalities().execute_with(|| {
+        let invalid_member_id = 44;
+
+        UpdateMembershipVerificationFixture::default()
+            .with_member_id(invalid_member_id)
+            .call_and_assert(Err(Error::<Test>::MemberProfileNotFound.into()));
+    });
+}
+
+#[test]
+fn update_verification_status_fails_with_invalid_worker_id() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = MembershipFee::get();
+        set_alice_free_balance(initial_balance);
+
+        let next_member_id = Membership::members_created();
+        assert_ok!(buy_default_membership_as_alice());
+
+        let invalid_worker_id = 44;
+
+        UpdateMembershipVerificationFixture::default()
+            .with_member_id(next_member_id)
+            .with_worker_id(invalid_worker_id)
+            .call_and_assert(Err(working_group::Error::<
+                Test,
+                MembershipWorkingGroupInstance,
+            >::WorkerDoesNotExist
+                .into()));
+    });
+}
+
+#[test]
+fn buy_membership_fails_with_invalid_name() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = MembershipFee::get();
+        set_alice_free_balance(initial_balance);
+
+        let name: [u8; 500] = [1; 500];
+
+        let buy_membership_fixture = BuyMembershipFixture::default().with_name(name.to_vec());
+
+        buy_membership_fixture.call_and_assert(Err(Error::<Test>::NameTooLong.into()));
+    });
+}
+
+#[test]
+fn buy_membership_fails_with_non_member_referrer_id() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = MembershipFee::get();
+        set_alice_free_balance(initial_balance);
+
+        let invalid_member_id = 111;
+
+        let buy_membership_fixture =
+            BuyMembershipFixture::default().with_referrer_id(invalid_member_id);
+
+        buy_membership_fixture.call_and_assert(Err(Error::<Test>::ReferrerIsNotMember.into()));
+    });
+}
+
+#[test]
+fn buy_membership_with_referral_cut_succeeds() {
+    let member_id = 0u64;
+    let initial_members = [(member_id, ALICE_ACCOUNT_ID)];
+
+    build_test_externalities_with_initial_members(initial_members.to_vec()).execute_with(|| {
+        let initial_balance = 10000;
+        increase_total_balance_issuance_using_account_id(BOB_ACCOUNT_ID, initial_balance);
+
+        let buy_membership_fixture = BuyMembershipFixture::default()
+            .with_handle(b"bobs_handle".to_vec())
+            .with_accounts(BOB_ACCOUNT_ID)
+            .with_origin(RawOrigin::Signed(BOB_ACCOUNT_ID))
+            .with_referrer_id(member_id);
+
+        buy_membership_fixture.call_and_assert(Ok(()));
+
+        let referral_cut = Membership::get_referral_bonus();
+
+        assert_eq!(Balances::usable_balance(&ALICE_ACCOUNT_ID), referral_cut);
+        assert_eq!(
+            Balances::usable_balance(&BOB_ACCOUNT_ID),
+            initial_balance - MembershipFee::get()
+        );
+    });
+}
+
+#[test]
+fn referral_bonus_calculated_successfully() {
+    build_test_externalities().execute_with(|| {
+        // it should take minimum of the referral cut and membership fee
+        let membership_fee = MembershipFee::get();
+        let diff = 10;
+
+        let referral_cut = membership_fee.saturating_sub(diff);
+        <crate::ReferralCut<Test>>::put(referral_cut);
+        assert_eq!(Membership::get_referral_bonus(), referral_cut);
+
+        let referral_cut = membership_fee.saturating_add(diff);
+        <crate::ReferralCut<Test>>::put(referral_cut);
+        assert_eq!(Membership::get_referral_bonus(), membership_fee);
+    });
+}
+
+#[test]
+fn set_referral_cut_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let starting_block = 1;
+        run_to_block(starting_block);
+
+        SetReferralCutFixture::default().call_and_assert(Ok(()));
+
+        EventFixture::assert_last_crate_event(Event::<Test>::ReferralCutUpdated(
+            DEFAULT_REFERRAL_CUT_VALUE,
+        ));
+    });
+}
+
+#[test]
+fn set_referral_fails_with_invalid_origin() {
+    build_test_externalities().execute_with(|| {
+        SetReferralCutFixture::default()
+            .with_origin(RawOrigin::Signed(ALICE_ACCOUNT_ID))
+            .call_and_assert(Err(DispatchError::BadOrigin));
+    });
+}

+ 1 - 2
runtime-modules/proposals/codex/Cargo.toml

@@ -15,7 +15,6 @@ frame-system = { package = 'frame-system', default-features = false, git = 'http
 staking = { package = 'pallet-staking', 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'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-membership = { package = 'pallet-membership', default-features = false, path = '../../membership'}
 governance = { package = 'pallet-governance', default-features = false, path = '../../governance'}
 minting = { package = 'pallet-token-mint', default-features = false, path = '../../token-minting'}
 working-group = { package = 'pallet-working-group', default-features = false, path = '../../working-group'}
@@ -23,6 +22,7 @@ common = { package = 'pallet-common', default-features = false, path = '../../co
 proposals-engine = { package = 'pallet-proposals-engine', default-features = false, path = '../engine'}
 proposals-discussion = { package = 'pallet-proposals-discussion', default-features = false, path = '../discussion'}
 constitution = { package = 'pallet-constitution', default-features = false, path = '../../constitution'}
+membership = { package = 'pallet-membership', default-features = false, path = '../../membership'}
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
@@ -46,7 +46,6 @@ std = [
     'staking/std',
     'pallet-timestamp/std',
     'balances/std',
-    'membership/std',
     'governance/std',
     'minting/std',
     'working-group/std',

+ 167 - 467
runtime-modules/proposals/codex/src/lib.rs

@@ -1,5 +1,5 @@
 //! # Proposals codex module
-//! Proposals `codex` module for the Joystream platform. Version 3.
+//! Proposals `codex` module for the Joystream platform.
 //! Component of the proposals system. It contains preset proposal types.
 //!
 //! ## Overview
@@ -42,17 +42,18 @@
 //! - [governance](../substrate_governance_module/index.html)
 //!
 //! ### Notes
-//! The module uses [ProposalEncoder](./trait.ProposalEncoder.html) to encode the proposal using
-//! its details. Encoded byte vector is passed to the _proposals engine_ as serialized executable code.
+//! The module uses [ProposalEncoder](./trait.ProposalEncoder.html) to encode the proposal using its
+//! details. Encoded byte vector is passed to the _proposals engine_ as serialized executable code.
 
 // `decl_module!` does a lot of recursion and requires us to increase the limit to 256.
 #![recursion_limit = "256"]
 // Ensure we're `no_std` when compiling for Wasm.
 #![cfg_attr(not(feature = "std"), no_std)]
-// Disable this lint warning because Substrate generates function without an alias for the ProposalDetailsOf type.
+// Disable this lint warning because Substrate generates function without an alias for
+// the ProposalDetailsOf type.
 #![allow(clippy::too_many_arguments)]
 
-mod proposal_types;
+mod types;
 
 #[cfg(test)]
 mod tests;
@@ -66,17 +67,16 @@ use sp_std::clone::Clone;
 use sp_std::str::from_utf8;
 use sp_std::vec::Vec;
 
-pub use crate::proposal_types::{
-    AddOpeningParameters, FillOpeningParameters, TerminateRoleParameters,
+pub use crate::types::{
+    AddOpeningParameters, FillOpeningParameters, GeneralProposalParams, ProposalDetails,
+    ProposalDetailsOf, ProposalEncoder, TerminateRoleParameters,
 };
 use common::origin::ActorOriginValidator;
-use common::working_group::WorkingGroup;
-pub use proposal_types::{ProposalDetails, ProposalDetailsOf, ProposalEncoder};
+use common::MemberId;
 use proposals_discussion::ThreadMode;
 use proposals_engine::{
     BalanceOf, ProposalCreationParameters, ProposalObserver, ProposalParameters,
 };
-use working_group::Penalty;
 
 // 'Set working group budget capacity' proposal limit
 const WORKING_GROUP_BUDGET_CAPACITY_MAX_VALUE: u32 = 5_000_000;
@@ -85,35 +85,15 @@ const MAX_SPENDING_PROPOSAL_VALUE: u32 = 5_000_000_u32;
 // Max validator count for the 'set validator count' proposal
 const MAX_VALIDATOR_COUNT: u32 = 100;
 
-// Data container struct to fix linter warning 'too many arguments for the function' for the
-// create_proposal() function.
-struct CreateProposalParameters<T: Trait> {
-    pub origin: T::Origin,
-    pub member_id: MemberId<T>,
-    pub title: Vec<u8>,
-    pub description: Vec<u8>,
-    pub staking_account_id: Option<T::AccountId>,
-    pub proposal_code: Vec<u8>,
-    pub proposal_parameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>,
-    pub proposal_details: ProposalDetailsOf<T>,
-    pub exact_execution_block: Option<T::BlockNumber>,
-}
-
 /// 'Proposals codex' substrate module Trait
 pub trait Trait:
     frame_system::Trait
     + proposals_engine::Trait
     + proposals_discussion::Trait
-    + membership::Trait
+    + common::Trait
     + governance::election::Trait
     + staking::Trait
 {
-    /// Defines max allowed text proposal length.
-    type TextProposalMaxLength: Get<u32>;
-
-    /// Defines max wasm code length of the runtime upgrade proposal.
-    type RuntimeUpgradeWasmProposalMaxLength: Get<u32>;
-
     /// Validates member id and origin combination.
     type MembershipOriginValidator: ActorOriginValidator<
         Self::Origin,
@@ -181,6 +161,13 @@ pub trait Trait:
     >;
 }
 
+/// Specialized alias of GeneralProposalParams
+pub type GeneralProposalParameters<T> = GeneralProposalParams<
+    MemberId<T>,
+    <T as frame_system::Trait>::AccountId,
+    <T as frame_system::Trait>::BlockNumber,
+>;
+
 /// Balance alias for GovernanceCurrency from `common` module. TODO: replace with BalanceOf
 pub type BalanceOfGovernanceCurrency<T> =
     <<T as common::currency::GovernanceCurrency>::Currency as Currency<
@@ -191,20 +178,12 @@ pub type BalanceOfGovernanceCurrency<T> =
 pub type BalanceOfMint<T> =
     <<T as minting::Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
 
-type MemberId<T> = <T as membership::Trait>::MemberId;
-
 decl_error! {
     /// Codex module predefined errors
     pub enum Error for Module<T: Trait> {
-        /// The size of the provided text for text proposal exceeded the limit
-        TextProposalSizeExceeded,
-
         /// Provided text for text proposal is empty
         TextProposalIsEmpty,
 
-        /// The size of the provided WASM code for the runtime upgrade proposal exceeded the limit
-        RuntimeProposalSizeExceeded,
-
         /// Provided WASM code for the runtime upgrade proposal is empty
         RuntimeProposalIsEmpty,
 
@@ -321,406 +300,58 @@ decl_module! {
         const AmendConstitutionProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
             = T::AmendConstitutionProposalParameters::get();
 
-        /// Exports max allowed text proposal length const.
-        const TextProposalMaxLength: u32 = T::TextProposalMaxLength::get();
-
-        /// Exports max wasm code length of the runtime upgrade proposal const.
-        const RuntimeUpgradeWasmProposalMaxLength: u32 = T::RuntimeUpgradeWasmProposalMaxLength::get();
-
-        /// Create 'Text (signal)' proposal type.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_text_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            text: Vec<u8>,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            ensure!(!text.is_empty(), Error::<T>::TextProposalIsEmpty);
-            ensure!(text.len() as u32 <=  T::TextProposalMaxLength::get(),
-                Error::<T>::TextProposalSizeExceeded);
-
-            let proposal_details = ProposalDetails::Text(text);
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::TextProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'Runtime upgrade' proposal type. Runtime upgrade can be initiated only by
-        /// members from the hardcoded list `RuntimeUpgradeProposalAllowedProposers`
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_runtime_upgrade_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            wasm: Vec<u8>,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            ensure!(!wasm.is_empty(), Error::<T>::RuntimeProposalIsEmpty);
-            ensure!(wasm.len() as u32 <= T::RuntimeUpgradeWasmProposalMaxLength::get(),
-                Error::<T>::RuntimeProposalSizeExceeded);
-
-            let proposal_details = ProposalDetails::RuntimeUpgrade(wasm);
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::RuntimeUpgradeProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'Spending' proposal type.
-        /// This proposal uses `spend_from_council_mint()` extrinsic from the `governance::council`  module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_spending_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            balance: BalanceOfMint<T>,
-            destination: T::AccountId,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            ensure!(balance != BalanceOfMint::<T>::zero(), Error::<T>::InvalidSpendingProposalBalance);
-            ensure!(
-                balance <= <BalanceOfMint<T>>::from(MAX_SPENDING_PROPOSAL_VALUE),
-                Error::<T>::InvalidSpendingProposalBalance
-            );
-
-            let proposal_details = ProposalDetails::Spending(balance, destination);
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::SpendingProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'Evict storage provider' proposal type.
-        /// This proposal uses `set_validator_count()` extrinsic from the Substrate `staking`  module.
+        /// Create a proposal, the type of proposal depends on the `proposal_details` variant
         #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_set_validator_count_proposal(
+        pub fn create_proposal(
             origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            new_validator_count: u32,
-            exact_execution_block: Option<T::BlockNumber>,
+            general_proposal_parameters: GeneralProposalParameters<T>,
+            proposal_details: ProposalDetailsOf<T>,
         ) {
-            ensure!(
-                new_validator_count >= <staking::Module<T>>::minimum_validator_count(),
-                Error::<T>::InvalidValidatorCount
-            );
-
-            ensure!(
-                new_validator_count <= MAX_VALIDATOR_COUNT,
-                Error::<T>::InvalidValidatorCount
-            );
-
-            let proposal_details = ProposalDetails::SetValidatorCount(new_validator_count);
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::SetValidatorCountProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
+            Self::ensure_details_checks(&proposal_details)?;
+
+            let proposal_parameters = Self::get_proposal_parameters(&proposal_details);
+            let proposal_code = T::ProposalEncoder::encode_proposal(proposal_details.clone());
+
+            let account_id =
+                T::MembershipOriginValidator::ensure_actor_origin(
+                    origin,
+                    general_proposal_parameters.member_id
+                )?;
+
+            <proposals_engine::Module<T>>::ensure_create_proposal_parameters_are_valid(
+                &proposal_parameters,
+                &general_proposal_parameters.title,
+                &general_proposal_parameters.description,
+                general_proposal_parameters.staking_account_id.clone(),
+                general_proposal_parameters.exact_execution_block,
+            )?;
+
+            let initial_thread_mode = ThreadMode::Open;
+            <proposals_discussion::Module<T>>::ensure_can_create_thread(&initial_thread_mode)?;
+
+            let discussion_thread_id = <proposals_discussion::Module<T>>::create_thread(
+                general_proposal_parameters.member_id,
+                initial_thread_mode,
+            )?;
+
+            let proposal_creation_params = ProposalCreationParameters {
+                account_id,
+                proposer_id: general_proposal_parameters.member_id,
+                proposal_parameters,
+                title: general_proposal_parameters.title,
+                description: general_proposal_parameters.description,
+                staking_account_id: general_proposal_parameters.staking_account_id,
+                encoded_dispatchable_call_code: proposal_code,
+                exact_execution_block: general_proposal_parameters.exact_execution_block,
             };
 
-            Self::create_proposal(params)?;
-        }
+            let proposal_id =
+                <proposals_engine::Module<T>>::create_proposal(proposal_creation_params)?;
 
-        /// Create 'Add working group leader opening' proposal type.
-        /// This proposal uses `add_opening()` extrinsic from the Joystream `working group` module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_add_working_group_leader_opening_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            add_opening_parameters: AddOpeningParameters<T::BlockNumber, BalanceOfGovernanceCurrency<T>>,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            let proposal_details = ProposalDetails::AddWorkingGroupLeaderOpening(add_opening_parameters);
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::AddWorkingGroupOpeningProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
+            <ThreadIdByProposalId<T>>::insert(proposal_id, discussion_thread_id);
+            <ProposalDetailsByProposalId<T>>::insert(proposal_id, proposal_details);
         }
 
-        /// Create 'Fill working group leader opening' proposal type.
-        /// This proposal uses `fill_opening()` extrinsic from the Joystream `working group` module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_fill_working_group_leader_opening_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            fill_opening_parameters: FillOpeningParameters,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            let proposal_details = ProposalDetails::FillWorkingGroupLeaderOpening(fill_opening_parameters);
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::FillWorkingGroupOpeningProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'Set working group budget capacity' proposal type.
-        /// This proposal uses `set_mint_capacity()` extrinsic from the `working-group`  module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_set_working_group_budget_capacity_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            mint_balance: BalanceOfMint<T>,
-            working_group: WorkingGroup,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            ensure!(
-                mint_balance <= <BalanceOfMint<T>>::from(WORKING_GROUP_BUDGET_CAPACITY_MAX_VALUE),
-                Error::<T>::InvalidWorkingGroupBudgetCapacity
-            );
-
-            let proposal_details = ProposalDetails::SetWorkingGroupBudgetCapacity(mint_balance, working_group);
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::SetWorkingGroupBudgetCapacityProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'decrease working group leader stake' proposal type.
-        /// This proposal uses `decrease_stake()` extrinsic from the `working-group`  module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_decrease_working_group_leader_stake_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            worker_id: working_group::WorkerId<T>,
-            decreasing_stake: BalanceOf<T>,
-            working_group: WorkingGroup,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            ensure!(decreasing_stake != Zero::zero(), Error::<T>::DecreasingStakeIsZero);
-
-            let proposal_details = ProposalDetails::DecreaseWorkingGroupLeaderStake(
-                worker_id,
-                decreasing_stake,
-                working_group
-            );
-
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::DecreaseWorkingGroupLeaderStakeProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'slash working group leader stake' proposal type.
-        /// This proposal uses `slash_stake()` extrinsic from the `working-group`  module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_slash_working_group_leader_stake_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            worker_id: working_group::WorkerId<T>,
-            penalty: Penalty<BalanceOf<T>>,
-            working_group: WorkingGroup,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            ensure!(penalty.slashing_amount != Zero::zero(), Error::<T>::SlashingStakeIsZero);
-
-            let proposal_details = ProposalDetails::SlashWorkingGroupLeaderStake(
-                worker_id,
-                penalty,
-                working_group
-            );
-
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::SlashWorkingGroupLeaderStakeProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'set working group leader reward' proposal type.
-        /// This proposal uses `update_reward_amount()` extrinsic from the `working-group`  module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_set_working_group_leader_reward_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            worker_id: working_group::WorkerId<T>,
-            reward_amount: Option<BalanceOfMint<T>>,
-            working_group: WorkingGroup,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            let proposal_details = ProposalDetails::SetWorkingGroupLeaderReward(
-                worker_id,
-                reward_amount,
-                working_group
-            );
-
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::SetWorkingGroupLeaderRewardProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'terminate working group leader role' proposal type.
-        /// This proposal uses `terminate_role()` extrinsic from the `working-group`  module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_terminate_working_group_leader_role_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            terminate_role_parameters: TerminateRoleParameters<working_group::WorkerId<T>, BalanceOf<T>>,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            let proposal_details = ProposalDetails::TerminateWorkingGroupLeaderRole(terminate_role_parameters);
-
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::TerminateWorkingGroupLeaderRoleProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-        /// Create 'amend constitution' proposal type.
-        /// This proposal uses `amend_constitution()` extrinsic from the `constitution`  module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_amend_constitution_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            constitution_text: Vec<u8>,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            let proposal_details = ProposalDetails::AmendConstitution(constitution_text);
-
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::AmendConstitutionProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
-
 // *************** Extrinsic to execute
 
         /// Text proposal extrinsic. Should be used as callable object to pass to the `engine` module.
@@ -756,45 +387,114 @@ decl_module! {
 }
 
 impl<T: Trait> Module<T> {
-    // Generic template proposal builder
-    fn create_proposal(params: CreateProposalParameters<T>) -> DispatchResult {
-        let account_id =
-            T::MembershipOriginValidator::ensure_actor_origin(params.origin, params.member_id)?;
-
-        <proposals_engine::Module<T>>::ensure_create_proposal_parameters_are_valid(
-            &params.proposal_parameters,
-            &params.title,
-            &params.description,
-            params.staking_account_id.clone(),
-            params.exact_execution_block,
-        )?;
-
-        let initial_thread_mode = ThreadMode::Open;
-        <proposals_discussion::Module<T>>::ensure_can_create_thread(&initial_thread_mode)?;
-
-        let discussion_thread_id = <proposals_discussion::Module<T>>::create_thread(
-            params.member_id,
-            initial_thread_mode,
-        )?;
-
-        let proposal_creation_params = ProposalCreationParameters {
-            account_id,
-            proposer_id: params.member_id,
-            proposal_parameters: params.proposal_parameters,
-            title: params.title,
-            description: params.description,
-            staking_account_id: params.staking_account_id,
-            encoded_dispatchable_call_code: params.proposal_code,
-            exact_execution_block: params.exact_execution_block,
-        };
-
-        let proposal_id = <proposals_engine::Module<T>>::create_proposal(proposal_creation_params)?;
-
-        <ThreadIdByProposalId<T>>::insert(proposal_id, discussion_thread_id);
-        <ProposalDetailsByProposalId<T>>::insert(proposal_id, params.proposal_details);
+    // Ensure that the proposal details respects all the checks
+    fn ensure_details_checks(details: &ProposalDetailsOf<T>) -> DispatchResult {
+        match details {
+            ProposalDetails::Text(ref text) => {
+                ensure!(!text.is_empty(), Error::<T>::TextProposalIsEmpty);
+            }
+            ProposalDetails::RuntimeUpgrade(ref wasm) => {
+                ensure!(!wasm.is_empty(), Error::<T>::RuntimeProposalIsEmpty);
+            }
+            ProposalDetails::Spending(ref balance, _) => {
+                ensure!(
+                    *balance != BalanceOfMint::<T>::zero(),
+                    Error::<T>::InvalidSpendingProposalBalance
+                );
+                ensure!(
+                    *balance <= <BalanceOfMint<T>>::from(MAX_SPENDING_PROPOSAL_VALUE),
+                    Error::<T>::InvalidSpendingProposalBalance
+                );
+            }
+            ProposalDetails::SetValidatorCount(ref new_validator_count) => {
+                ensure!(
+                    *new_validator_count >= <staking::Module<T>>::minimum_validator_count(),
+                    Error::<T>::InvalidValidatorCount
+                );
+
+                ensure!(
+                    *new_validator_count <= MAX_VALIDATOR_COUNT,
+                    Error::<T>::InvalidValidatorCount
+                );
+            }
+            ProposalDetails::AddWorkingGroupLeaderOpening(..) => {
+                // Note: No checks for this proposal for now
+            }
+            ProposalDetails::FillWorkingGroupLeaderOpening(..) => {
+                // Note: No checks for this proposal for now
+            }
+            ProposalDetails::SetWorkingGroupBudgetCapacity(ref mint_balance, _) => {
+                ensure!(
+                    *mint_balance
+                        <= <BalanceOfMint<T>>::from(WORKING_GROUP_BUDGET_CAPACITY_MAX_VALUE),
+                    Error::<T>::InvalidWorkingGroupBudgetCapacity
+                );
+            }
+            ProposalDetails::DecreaseWorkingGroupLeaderStake(_, ref decreasing_stake, _) => {
+                ensure!(
+                    *decreasing_stake != Zero::zero(),
+                    Error::<T>::DecreasingStakeIsZero
+                );
+            }
+            ProposalDetails::SlashWorkingGroupLeaderStake(_, ref penalty, _) => {
+                ensure!(
+                    penalty.slashing_amount != Zero::zero(),
+                    Error::<T>::SlashingStakeIsZero
+                );
+            }
+            ProposalDetails::SetWorkingGroupLeaderReward(..) => {
+                // Note: No checks for this proposal for now
+            }
+            ProposalDetails::TerminateWorkingGroupLeaderRole(..) => {
+                // Note: No checks for this proposal for now
+            }
+            ProposalDetails::AmendConstitution(..) => {
+                // Note: No checks for this proposal for now
+            }
+        }
 
         Ok(())
     }
+
+    // Returns the proposal parameters according to ProposalDetials
+    fn get_proposal_parameters(
+        details: &ProposalDetailsOf<T>,
+    ) -> ProposalParameters<T::BlockNumber, BalanceOf<T>> {
+        match details {
+            ProposalDetailsOf::<T>::Text(..) => T::TextProposalParameters::get(),
+            ProposalDetailsOf::<T>::RuntimeUpgrade(..) => {
+                T::RuntimeUpgradeProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::Spending(..) => T::SpendingProposalParameters::get(),
+            ProposalDetailsOf::<T>::SetValidatorCount(..) => {
+                T::SetValidatorCountProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::AddWorkingGroupLeaderOpening(..) => {
+                T::AddWorkingGroupOpeningProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::FillWorkingGroupLeaderOpening(..) => {
+                T::FillWorkingGroupOpeningProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::SetWorkingGroupBudgetCapacity(..) => {
+                T::SetWorkingGroupBudgetCapacityProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::DecreaseWorkingGroupLeaderStake(..) => {
+                T::DecreaseWorkingGroupLeaderStakeProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::SlashWorkingGroupLeaderStake(..) => {
+                T::SlashWorkingGroupLeaderStakeProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::SetWorkingGroupLeaderReward(..) => {
+                T::SetWorkingGroupLeaderRewardProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::TerminateWorkingGroupLeaderRole(..) => {
+                T::TerminateWorkingGroupLeaderRoleProposalParameters::get()
+            }
+            ProposalDetailsOf::<T>::AmendConstitution(..) => {
+                T::AmendConstitutionProposalParameters::get()
+            }
+        }
+    }
 }
 
 impl<T: Trait> ProposalObserver<T> for Module<T> {

+ 16 - 10
runtime-modules/proposals/codex/src/tests/mock.rs

@@ -8,7 +8,7 @@ use sp_runtime::curve::PiecewiseLinear;
 use sp_runtime::{
     testing::Header,
     traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
+    DispatchResult, Perbill,
 };
 use sp_staking::SessionIndex;
 use staking_handler::{LockComparator, StakingManager};
@@ -45,11 +45,24 @@ impl common::currency::GovernanceCurrency for Test {
     type Currency = balances::Module<Self>;
 }
 
-impl membership::Trait for Test {
-    type Event = ();
+impl common::Trait for Test {
     type MemberId = u64;
     type ActorId = u64;
+}
+
+impl membership::Trait for Test {
+    type Event = ();
     type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
+}
+
+impl common::working_group::WorkingGroupIntegration<Test> for () {
+    fn ensure_worker_origin(
+        _origin: <Test as frame_system::Trait>::Origin,
+        _worker_id: &<Test as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        unimplemented!();
+    }
 }
 
 parameter_types! {
@@ -192,11 +205,6 @@ impl VotersParameters for MockVotersParameters {
     }
 }
 
-parameter_types! {
-    pub const TextProposalMaxLength: u32 = 20_000;
-    pub const RuntimeUpgradeWasmProposalMaxLength: u32 = 20_000;
-}
-
 impl governance::election::Trait for Test {
     type Event = ();
     type CouncilElected = ();
@@ -395,8 +403,6 @@ pub(crate) fn default_proposal_parameters() -> ProposalParameters<u64, u64> {
 }
 
 impl crate::Trait for Test {
-    type TextProposalMaxLength = TextProposalMaxLength;
-    type RuntimeUpgradeWasmProposalMaxLength = RuntimeUpgradeWasmProposalMaxLength;
     type MembershipOriginValidator = ();
     type ProposalEncoder = ();
     type SetValidatorCountProposalParameters = DefaultProposalParameters;

Diferenças do arquivo suprimidas por serem muito extensas
+ 370 - 339
runtime-modules/proposals/codex/src/tests/mod.rs


+ 23 - 3
runtime-modules/proposals/codex/src/proposal_types/mod.rs → runtime-modules/proposals/codex/src/types.rs

@@ -7,7 +7,7 @@ use sp_std::vec::Vec;
 
 use common::working_group::WorkingGroup;
 
-use working_group::{Penalty, RewardPolicy, StakePolicy};
+use working_group::{Penalty, StakePolicy};
 
 /// Encodes proposal using its details information.
 pub trait ProposalEncoder<T: crate::Trait> {
@@ -88,6 +88,26 @@ impl<MintedBalance, CurrencyBalance, BlockNumber, AccountId, StakeBalance, Worke
     }
 }
 
+/// Proposal parameters common to all proposals
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, Eq)]
+pub struct GeneralProposalParams<MemberId, AccountId, BlockNumber> {
+    /// Member ID of proposer
+    pub member_id: MemberId,
+
+    /// Title of the proposal
+    pub title: Vec<u8>,
+
+    /// Proposal description
+    pub description: Vec<u8>,
+
+    /// Staking Account Id for proposer, must have one for proposal to work
+    pub staking_account_id: Option<AccountId>,
+
+    /// Intended execution block for the proposal
+    pub exact_execution_block: Option<BlockNumber>,
+}
+
 /// Parameters for the 'terminate the leader position' proposal.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Debug)]
@@ -126,8 +146,8 @@ pub struct AddOpeningParameters<BlockNumber, Balance> {
     /// Stake policy for the opening.
     pub stake_policy: Option<StakePolicy<BlockNumber, Balance>>,
 
-    /// Reward policy for the opening.
-    pub reward_policy: Option<RewardPolicy<Balance>>,
+    /// Reward per block for the opening.
+    pub reward_per_block: Option<Balance>,
 
     /// Defines working group with the open position.
     pub working_group: WorkingGroup,

+ 6 - 3
runtime-modules/proposals/discussion/Cargo.toml

@@ -10,10 +10,12 @@ codec = { package = 'parity-scale-codec', version = '1.3.4', default-features =
 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'}
-membership = { package = 'pallet-membership', default-features = false, path = '../../membership'}
 common = { package = 'pallet-common', default-features = false, path = '../../common'}
+
+# Benchmarking dependencies
 frame-benchmarking = { package = 'frame-benchmarking', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca', optional = true}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca', optional = true}
+membership = { package = 'pallet-membership', default-features = false, path = '../../membership', optional = true}
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
@@ -21,12 +23,14 @@ sp-core = { package = 'sp-core', default-features = false, git = 'https://github
 sp-runtime = { package = 'sp-runtime', 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'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+membership = { package = 'pallet-membership', default-features = false, path = '../../membership'}
 
 [features]
 default = ['std']
 runtime-benchmarks = [
     'frame-benchmarking',
-    'balances'
+    'balances',
+	'membership'
 ]
 std = [
 	'serde',
@@ -34,6 +38,5 @@ std = [
 	'sp-std/std',
 	'frame-support/std',
 	'frame-system/std',
-    'membership/std',
     'common/std',
 ]

+ 13 - 9
runtime-modules/proposals/discussion/src/benchmarking.rs

@@ -45,7 +45,7 @@ fn assert_last_event<T: Trait>(generic_event: <T as Trait>::Event) {
     assert_eq!(event, &system_event);
 }
 
-fn member_account<T: membership::Trait + balances::Trait>(
+fn member_account<T: common::Trait + balances::Trait + membership::Trait>(
     name: &'static str,
     id: u32,
 ) -> (T::AccountId, T::MemberId) {
@@ -60,13 +60,17 @@ fn member_account<T: membership::Trait + balances::Trait>(
         "Balance not added",
     );
 
-    Membership::<T>::buy_membership(
-        RawOrigin::Signed(account_id.clone()).into(),
-        Some(handle),
-        None,
-        None,
-    )
-    .unwrap();
+    let params = membership::BuyMembershipParameters {
+        root_account: account_id.clone(),
+        controller_account: account_id.clone(),
+        name: None,
+        handle: Some(handle),
+        avatar_uri: None,
+        about: None,
+        referrer_id: None,
+    };
+
+    Membership::<T>::buy_membership(RawOrigin::Signed(account_id.clone()).into(), params).unwrap();
 
     (account_id, T::MemberId::from(id.try_into().unwrap()))
 }
@@ -74,7 +78,7 @@ fn member_account<T: membership::Trait + balances::Trait>(
 const MAX_BYTES: u32 = 16384;
 
 benchmarks! {
-    where_clause { where T: balances::Trait }
+    where_clause { where T: balances::Trait, T: membership::Trait }
     _ { }
 
     add_post {

+ 11 - 10
runtime-modules/proposals/discussion/src/lib.rs

@@ -1,21 +1,23 @@
 //! # Proposals discussion module
-//! Proposals `discussion` module for the Joystream platform. Version 3.
+//! Proposals `discussion` module for the Joystream platform.
 //! It contains discussion subsystem of the proposals.
 //!
 //! ## Overview
 //!
-//! The proposals discussion module is used by the codex module to provide a platform for discussions
-//! about different proposals. It allows to create discussion threads and then add and update related
-//! posts.
+//! The proposals discussion module is used by the codex module to provide a platform for
+//! discussions about different proposals. It allows to create discussion threads and then add and
+//! update related posts.
 //!
 //! ## Supported extrinsics
 //! - [add_post](./struct.Module.html#method.add_post) - adds a post to an existing discussion thread
 //! - [update_post](./struct.Module.html#method.update_post) - updates existing post
-//! - [change_thread_mode](./struct.Module.html#method.change_thread_mode) - changes thread permission mode
+//! - [change_thread_mode](./struct.Module.html#method.change_thread_mode) - changes thread
+//! permission mode
 //!
 //! ## Public API methods
 //! - [create_thread](./struct.Module.html#method.create_thread) - creates a discussion thread
-//! - [ensure_can_create_thread](./struct.Module.html#method.ensure_can_create_thread) - ensures safe thread creation
+//! - [ensure_can_create_thread](./struct.Module.html#method.ensure_can_create_thread) - ensures
+//! safe thread creation
 //!
 //! ## Usage
 //!
@@ -24,7 +26,7 @@
 //! use frame_system::ensure_root;
 //! use pallet_proposals_discussion::{self as discussions, ThreadMode};
 //!
-//! pub trait Trait: discussions::Trait + membership::Trait {}
+//! pub trait Trait: discussions::Trait + common::Trait {}
 //!
 //! decl_module! {
 //!     pub struct Module<T: Trait> for enum Call where origin: T::Origin {
@@ -61,12 +63,11 @@ use sp_std::clone::Clone;
 use sp_std::vec::Vec;
 
 use common::origin::ActorOriginValidator;
+use common::MemberId;
 use types::{DiscussionPost, DiscussionThread};
 
 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 {
@@ -106,7 +107,7 @@ pub trait CouncilMembership<AccountId, MemberId> {
 }
 
 /// 'Proposal discussion' substrate module Trait
-pub trait Trait: frame_system::Trait + membership::Trait {
+pub trait Trait: frame_system::Trait + common::Trait {
     /// Discussion event type.
     type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
 

+ 16 - 3
runtime-modules/proposals/discussion/src/tests/mock.rs

@@ -8,7 +8,7 @@ use sp_core::H256;
 use sp_runtime::{
     testing::Header,
     traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
+    DispatchResult, Perbill,
 };
 
 use crate::ActorOriginValidator;
@@ -73,11 +73,24 @@ impl common::currency::GovernanceCurrency for Test {
     type Currency = balances::Module<Self>;
 }
 
-impl membership::Trait for Test {
-    type Event = TestEvent;
+impl common::Trait for Test {
     type MemberId = u64;
     type ActorId = u64;
+}
+
+impl membership::Trait for Test {
+    type Event = TestEvent;
     type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
+}
+
+impl common::working_group::WorkingGroupIntegration<Test> for () {
+    fn ensure_worker_origin(
+        _origin: <Test as frame_system::Trait>::Origin,
+        _worker_id: &<Test as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        unimplemented!();
+    }
 }
 
 impl crate::Trait for Test {

+ 3 - 2
runtime-modules/proposals/engine/Cargo.toml

@@ -14,7 +14,6 @@ pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git
 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'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-membership = { package = 'pallet-membership', default-features = false, path = '../../membership'}
 common = { package = 'pallet-common', default-features = false, path = '../../common'}
 staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../../staking-handler'}
 
@@ -23,6 +22,7 @@ frame-benchmarking = { package = 'frame-benchmarking', default-features = false,
 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}
+membership = { package = 'pallet-membership', default-features = false, path = '../../membership', optional = true}
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
@@ -30,6 +30,7 @@ sp-core = { package = 'sp-core', default-features = false, git = 'https://github
 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'}
+membership = { package = 'pallet-membership', default-features = false, path = '../../membership'}
 
 [features]
 default = ['std']
@@ -38,6 +39,7 @@ runtime-benchmarks = [
     'governance',
     'recurringrewards',
     'minting',
+    'membership',
 ]
 std = [
 	'serde',
@@ -49,7 +51,6 @@ std = [
 	'sp-arithmetic/std',
 	'sp-runtime/std',
 	'balances/std',
-    'membership/std',
     'common/std',
     'staking-handler/std',
 ]

+ 21 - 12
runtime-modules/proposals/engine/src/benchmarking.rs

@@ -64,27 +64,34 @@ fn assert_in_events<T: Trait>(generic_event: <T as Trait>::Event) {
     }));
 }
 
-fn member_funded_account<T: Trait>(name: &'static str, id: u32) -> (T::AccountId, T::MemberId) {
+fn member_funded_account<T: Trait + membership::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);
 
     // Give balance for buying membership
     let _ = Balances::<T>::make_free_balance_be(&account_id, T::Balance::max_value());
 
-    Membership::<T>::buy_membership(
-        RawOrigin::Signed(account_id.clone()).into(),
-        Some(handle),
-        None,
-        None,
-    )
-    .unwrap();
+    let params = membership::BuyMembershipParameters {
+        root_account: account_id.clone(),
+        controller_account: account_id.clone(),
+        name: None,
+        handle: Some(handle),
+        avatar_uri: None,
+        about: None,
+        referrer_id: None,
+    };
+
+    Membership::<T>::buy_membership(RawOrigin::Signed(account_id.clone()).into(), params).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>(
+fn create_proposal<T: Trait + membership::Trait>(
     id: u32,
     proposal_number: u32,
     constitutionality: u32,
@@ -154,7 +161,9 @@ fn create_proposal<T: Trait>(
     (account_id, member_id, proposal_id)
 }
 
-fn create_multiple_finalized_proposals<T: Trait + governance::council::Trait>(
+fn create_multiple_finalized_proposals<
+    T: Trait + governance::council::Trait + membership::Trait,
+>(
     number_of_proposals: u32,
     constitutionality: u32,
     vote_kind: VoteKind,
@@ -203,7 +212,7 @@ const MAX_BYTES: u32 = 16384;
 benchmarks! {
     // Note: this is the syntax for this macro can't use "+"
     where_clause {
-        where T: governance::council::Trait
+        where T: governance::council::Trait, T: membership::Trait
     }
 
     _ { }
@@ -459,7 +468,7 @@ benchmarks! {
             );
 
             assert_eq!(proposal.status, status);
-            assert_eq!(proposal.current_constitutionality_level, 1);
+            assert_eq!(proposal.nr_of_council_confirmations, 1);
             assert_in_events::<T>(
                 RawEvent::ProposalStatusUpdated(proposal_id.clone(), status).into()
             );

+ 52 - 30
runtime-modules/proposals/engine/src/lib.rs

@@ -1,22 +1,25 @@
 //! # Proposals engine module
-//! Proposals `engine` module for the Joystream platform. Version 3.
+//! Proposals `engine` module for the Joystream platform.
 //! The main component of the proposals system. Provides methods and extrinsics to create and
 //! vote for proposals, inspired by Parity **Democracy module**.
 //!
 //! ## Overview
-//! Proposals `engine` module provides an abstract mechanism to work with proposals: creation, voting,
-//! execution, canceling, etc. Proposal execution demands serialized _Dispatchable_ proposal code.
-//! It could be any _Dispatchable_ + _Parameter_ type, but most likely, it would be serialized (via
-//! Parity _codec_ crate) extrisic call. A proposal stage can be described by its [status](./enum.ProposalStatus.html).
+//! Proposals `engine` module provides an abstract mechanism to work with proposals: creation,
+//! voting, execution, canceling, etc. Proposal execution demands serialized _Dispatchable_ proposal
+//! code. It could be any _Dispatchable_ + _Parameter_ type, but most likely, it would be serialized
+//! (via Parity _codec_ crate) extrisic call. A proposal stage can be described by
+//! its [status](./enum.ProposalStatus.html).
 //!
 //! ## Proposal lifecycle
 //! When a proposal passes [checks](./struct.Module.html#method.ensure_create_proposal_parameters_are_valid)
-//! for its [parameters](./struct.ProposalParameters.html) - it can be [created](./struct.Module.html#method.create_proposal).
-//! The newly created proposal has _Active_ status. The proposal can be voted on, vetoed or canceled during its
-//! _voting period_. Votes can be [different](./enum.VoteKind.html). When the proposal gets enough votes
-//! to be approved - the proposal becomes _PendingExecution_ or _PendingConstitutionality_. The proposal
-//! could also be slashed or rejected. If the _voting period_ ends with no decision it becomes expired.
-//! If the proposal got approved and _grace period_ passed - the  `engine` module tries to execute the proposal.
+//! for its [parameters](./struct.ProposalParameters.html) -
+//! it can be [created](./struct.Module.html#method.create_proposal).
+//! The newly created proposal has _Active_ status. The proposal can be voted on, vetoed or
+//! canceled during its _voting period_. Votes can be [different](./enum.VoteKind.html). When the
+//! proposal gets enough votes to be approved - the proposal becomes _PendingExecution_ or
+//! _PendingConstitutionality_. The proposal could also be slashed or rejected. If the _voting
+//! period_ ends with no decision it becomes expired. If the proposal got approved
+//! and _grace period_ passed - the  `engine` module tries to execute the proposal.
 //!
 //! ### Notes
 //!
@@ -24,7 +27,8 @@
 //! anytime before the proposal execution by the _sudo_.
 //! - If the _council_ got reelected during the proposal _voting period_ the external handler calls
 //! [reject_active_proposals](./trait.Module.html#method.reject_active_proposals) function and
-//! all active proposals got rejected and it also calls [reactivate_pending_constitutionality_proposals](./trait.Module.html#method.reactivate_pending_constitutionality_proposals)
+//! all active proposals got rejected and it also calls
+//! [reactivate_pending_constitutionality_proposals](./trait.Module.html#method.reactivate_pending_constitutionality_proposals)
 //! and proposals with pending constitutionality become active again.
 //! - There are different fees to apply for slashed, rejected, expired or cancelled proposals.
 //!
@@ -42,14 +46,19 @@
 //!
 //! ### Supported extrinsics
 //! - [vote](./struct.Module.html#method.vote) - registers a vote for the proposal
-//! - [cancel_proposal](./struct.Module.html#method.cancel_proposal) - cancels the proposal (can be canceled only by owner)
+//! - [cancel_proposal](./struct.Module.html#method.cancel_proposal) - cancels the proposal
+//! (can be canceled only by owner)
 //! - [veto_proposal](./struct.Module.html#method.veto_proposal) - vetoes the proposal
 //!
 //! ### Public API
-//! - [create_proposal](./struct.Module.html#method.create_proposal) - creates proposal using provided parameters
-//! - [ensure_create_proposal_parameters_are_valid](./struct.Module.html#method.ensure_create_proposal_parameters_are_valid) - ensures that we can create the proposal
-//! - [reject_active_proposals](./trait.Module.html#method.reject_active_proposals) - rejects all active proposals.
-//! - [reactivate_pending_constitutionality_proposals](./trait.Module.html#method.reactivate_pending_constitutionality_proposals) - reactivate proposals with pending constitutionality.
+//! - [create_proposal](./struct.Module.html#method.create_proposal) - creates proposal using
+//! provided parameters
+//! - [ensure_create_proposal_parameters_are_valid](./struct.Module.html#method.ensure_create_proposal_parameters_are_valid)
+//! - ensures that we can create the proposal
+//! - [reject_active_proposals](./trait.Module.html#method.reject_active_proposals) - rejects all
+//! active proposals.
+//! - [reactivate_pending_constitutionality_proposals](./trait.Module.html#method.reactivate_pending_constitutionality_proposals)
+//! - reactivate proposals with pending constitutionality.
 //!
 //! ## Usage
 //!
@@ -59,7 +68,7 @@
 //! use codec::Encode;
 //! use pallet_proposals_engine::{self as engine, ProposalParameters, ProposalCreationParameters};
 //!
-//! pub trait Trait: engine::Trait + membership::Trait {}
+//! pub trait Trait: engine::Trait + common::Trait {}
 //!
 //! decl_module! {
 //!     pub struct Module<T: Trait> for enum Call where origin: T::Origin {
@@ -109,7 +118,7 @@
 // Ensure we're `no_std` when compiling for Wasm.
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use types::{MemberId, ProposalOf};
+use types::ProposalOf;
 
 pub use types::{
     ApprovedProposalDecision, BalanceOf, ExecutionStatus, Proposal, ProposalCodeDecoder,
@@ -137,6 +146,7 @@ use sp_arithmetic::traits::{SaturatedConversion, Saturating, Zero};
 use sp_std::vec::Vec;
 
 use common::origin::ActorOriginValidator;
+use common::MemberId;
 use staking_handler::StakingHandler;
 
 /// Proposals engine WeightInfo.
@@ -156,7 +166,7 @@ type WeightInfoEngine<T> = <T as Trait>::WeightInfo;
 
 /// Proposals engine trait.
 pub trait Trait:
-    frame_system::Trait + pallet_timestamp::Trait + membership::Trait + balances::Trait
+    frame_system::Trait + pallet_timestamp::Trait + common::Trait + balances::Trait
 {
     /// Engine event type.
     type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
@@ -322,6 +332,9 @@ decl_error! {
 
         /// There is not enough balance for a stake.
         InsufficientBalanceForStake,
+
+        /// The conflicting stake discovered. Cannot stake.
+        ConflictingStakes,
     }
 }
 
@@ -436,7 +449,9 @@ decl_module! {
 
             proposal.voting_results.add_vote(vote.clone());
 
-            // mutation
+            //
+            // == MUTATION SAFE ==
+            //
 
             <Proposals<T>>::insert(proposal_id, proposal);
             <VoteExistsByProposalByVoter<T>>::insert(proposal_id, voter_id, vote.clone());
@@ -464,7 +479,9 @@ decl_module! {
             ensure!(matches!(proposal.status, ProposalStatus::Active{..}), Error::<T>::ProposalFinalized);
             ensure!(proposal.voting_results.no_votes_yet(), Error::<T>::ProposalHasVotes);
 
-            // mutation
+            //
+            // == MUTATION SAFE ==
+            //
 
             Self::finalize_proposal(proposal_id, proposal, ProposalDecision::Canceled);
         }
@@ -490,7 +507,9 @@ decl_module! {
                 Error::<T>::ProposalFinalized
             );
 
-            // mutation
+            //
+            // == MUTATION SAFE ==
+            //
 
             Self::finalize_proposal(proposal_id, proposal, ProposalDecision::Vetoed);
         }
@@ -516,8 +535,9 @@ impl<T: Trait> Module<T> {
             creation_params.exact_execution_block,
         )?;
 
-        // checks passed
-        // mutation
+        //
+        // == MUTATION SAFE ==
+        //
 
         let next_proposal_count_value = Self::proposal_count() + 1;
         let new_proposal_id = next_proposal_count_value;
@@ -527,11 +547,8 @@ impl<T: Trait> Module<T> {
         if let Some(stake_balance) = creation_params.proposal_parameters.required_stake {
             if let Some(staking_account_id) = creation_params.staking_account_id.clone() {
                 T::StakingHandler::lock(&staking_account_id, stake_balance);
-            } else {
-                // Return an error if no staking account provided.
-                return Err(Error::<T>::EmptyStake.into());
             }
-        };
+        }
 
         let new_proposal = Proposal {
             activated_at: Self::current_block(),
@@ -540,7 +557,7 @@ impl<T: Trait> Module<T> {
             status: ProposalStatus::Active,
             voting_results: VotingResults::default(),
             exact_execution_block: creation_params.exact_execution_block,
-            current_constitutionality_level: 0,
+            nr_of_council_confirmations: 0,
             staking_account_id: creation_params.staking_account_id,
         };
 
@@ -609,6 +626,11 @@ impl<T: Trait> Module<T> {
 
         if let Some(stake_balance) = parameters.required_stake {
             if let Some(staking_account_id) = staking_account_id {
+                ensure!(
+                    T::StakingHandler::is_account_free_of_conflicting_stakes(&staking_account_id),
+                    Error::<T>::ConflictingStakes
+                );
+
                 ensure!(
                     T::StakingHandler::is_enough_balance_for_stake(
                         &staking_account_id,

+ 18 - 8
runtime-modules/proposals/engine/src/tests/mock/mod.rs

@@ -13,7 +13,7 @@ use sp_core::H256;
 use sp_runtime::{
     testing::Header,
     traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
+    DispatchResult, Perbill,
 };
 
 pub(crate) mod proposals;
@@ -82,11 +82,24 @@ parameter_types! {
     pub const MembershipFee: u64 = 100;
 }
 
-impl membership::Trait for Test {
-    type Event = TestEvent;
+impl common::Trait for Test {
     type MemberId = u64;
     type ActorId = u64;
+}
+
+impl membership::Trait for Test {
+    type Event = TestEvent;
     type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
+}
+
+impl common::working_group::WorkingGroupIntegration<Test> for () {
+    fn ensure_worker_origin(
+        _origin: <Test as frame_system::Trait>::Origin,
+        _worker_id: &<Test as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        unimplemented!();
+    }
 }
 
 impl crate::Trait for Test {
@@ -226,11 +239,8 @@ impl minting::Trait for Test {
 }
 
 impl LockComparator<<Test as balances::Trait>::Balance> for Test {
-    fn are_locks_conflicting(
-        _new_lock: &LockIdentifier,
-        _existing_locks: &[LockIdentifier],
-    ) -> bool {
-        false
+    fn are_locks_conflicting(new_lock: &LockIdentifier, existing_locks: &[LockIdentifier]) -> bool {
+        existing_locks.iter().any(|l| l == new_lock)
     }
 }
 

+ 48 - 12
runtime-modules/proposals/engine/src/tests/mod.rs

@@ -6,7 +6,9 @@ use mock::*;
 
 use codec::Encode;
 use frame_support::dispatch::DispatchResult;
-use frame_support::traits::{Currency, OnFinalize, OnInitialize};
+use frame_support::traits::{
+    Currency, LockableCurrency, OnFinalize, OnInitialize, WithdrawReasons,
+};
 use frame_support::{StorageDoubleMap, StorageMap, StorageValue};
 use frame_system::RawOrigin;
 use frame_system::{EventRecord, Phase};
@@ -895,7 +897,7 @@ fn proposal_execution_postponed_because_of_grace_period() {
                     slashes: 0,
                 },
                 exact_execution_block: None,
-                current_constitutionality_level: 1,
+                nr_of_council_confirmations: 1,
                 staking_account_id: None,
             }
         );
@@ -941,7 +943,7 @@ fn proposal_execution_vetoed_successfully_during_the_grace_period() {
                     slashes: 0,
                 },
                 exact_execution_block: None,
-                current_constitutionality_level: 1,
+                nr_of_council_confirmations: 1,
                 staking_account_id: None,
             }
         );
@@ -992,7 +994,7 @@ fn proposal_execution_succeeds_after_the_grace_period() {
                 slashes: 0,
             },
             exact_execution_block: None,
-            current_constitutionality_level: 1,
+            nr_of_council_confirmations: 1,
             staking_account_id: None,
         };
 
@@ -1057,7 +1059,7 @@ fn create_dummy_proposal_succeeds_with_stake() {
                 activated_at: 0,
                 voting_results: VotingResults::default(),
                 exact_execution_block: None,
-                current_constitutionality_level: 0,
+                nr_of_council_confirmations: 0,
                 staking_account_id: Some(1),
                 status: ProposalStatus::Active,
             }
@@ -1084,7 +1086,7 @@ fn create_dummy_proposal_fail_with_stake_on_empty_account() {
 }
 
 #[test]
-fn create_proposal_fais_with_insufficient_stake_parameters() {
+fn create_proposal_fails_with_insufficient_stake_parameters() {
     initial_test_ext().execute_with(|| {
         let parameters_fixture = ProposalParametersFixture::default();
 
@@ -1104,6 +1106,40 @@ fn create_proposal_fais_with_insufficient_stake_parameters() {
     });
 }
 
+#[test]
+fn create_proposal_fails_with_empty_stake() {
+    initial_test_ext().execute_with(|| {
+        let parameters_fixture = ProposalParametersFixture::default().with_required_stake(300);
+        let dummy_proposal =
+            DummyProposalFixture::default().with_parameters(parameters_fixture.params());
+
+        dummy_proposal.create_proposal_and_assert(Err(Error::<Test>::EmptyStake.into()));
+    });
+}
+
+#[test]
+fn create_proposal_fails_with_conflicting_stakes() {
+    initial_test_ext().execute_with(|| {
+        let staking_account_id = 1;
+
+        let initial_balance = 100000;
+        increase_total_balance_issuance_using_account_id(staking_account_id, initial_balance);
+        Balances::set_lock(
+            LockId::get(),
+            &staking_account_id,
+            100,
+            WithdrawReasons::all(),
+        );
+
+        let parameters_fixture = ProposalParametersFixture::default().with_required_stake(300);
+        let dummy_proposal = DummyProposalFixture::default()
+            .with_parameters(parameters_fixture.params())
+            .with_stake(staking_account_id);
+
+        dummy_proposal.create_proposal_and_assert(Err(Error::<Test>::ConflictingStakes.into()));
+    });
+}
+
 #[test]
 fn finalize_expired_proposal_and_check_stake_removing_with_balance_checks_succeeds() {
     initial_test_ext().execute_with(|| {
@@ -1155,7 +1191,7 @@ fn finalize_expired_proposal_and_check_stake_removing_with_balance_checks_succee
             status: ProposalStatus::Active,
             voting_results: VotingResults::default(),
             exact_execution_block: None,
-            current_constitutionality_level: 0,
+            nr_of_council_confirmations: 0,
             staking_account_id: Some(1),
         };
 
@@ -1227,7 +1263,7 @@ fn proposal_cancellation_with_slashes_with_balance_checks_succeeds() {
             status: ProposalStatus::Active,
             voting_results: VotingResults::default(),
             exact_execution_block: None,
-            current_constitutionality_level: 0,
+            nr_of_council_confirmations: 0,
             staking_account_id: Some(1),
         };
 
@@ -1551,7 +1587,7 @@ fn proposal_execution_with_exact_execution_works() {
                     slashes: 0,
                 },
                 exact_execution_block: Some(exact_block),
-                current_constitutionality_level: 1,
+                nr_of_council_confirmations: 1,
                 staking_account_id: None,
             }
         );
@@ -1623,7 +1659,7 @@ fn proposal_with_pending_constitutionality_succeeds() {
                     slashes: 0,
                 },
                 exact_execution_block: None,
-                current_constitutionality_level: 1,
+                nr_of_council_confirmations: 1,
                 staking_account_id: None,
             }
         );
@@ -1686,7 +1722,7 @@ fn proposal_with_pending_constitutionality_reactivation_succeeds() {
                     slashes: 0,
                 },
                 exact_execution_block: None,
-                current_constitutionality_level: 1,
+                nr_of_council_confirmations: 1,
                 staking_account_id: None,
             }
         );
@@ -1787,7 +1823,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
                     slashes: 0,
                 },
                 exact_execution_block: None,
-                current_constitutionality_level: 1,
+                nr_of_council_confirmations: 1,
                 staking_account_id: Some(account_id),
             }
         );

+ 5 - 7
runtime-modules/proposals/engine/src/types/mod.rs

@@ -13,6 +13,8 @@ use sp_std::cmp::PartialOrd;
 use sp_std::ops::Add;
 use sp_std::vec::Vec;
 
+use common::MemberId;
+
 mod proposal_statuses;
 
 pub use proposal_statuses::{
@@ -138,7 +140,7 @@ pub struct Proposal<BlockNumber, ProposerId, Balance, AccountId> {
 
     /// The number of councils in that must approve the proposal in a row before it has its
     /// intended effect.
-    pub current_constitutionality_level: u32,
+    pub nr_of_council_confirmations: u32,
 
     /// Optional account id for staking.
     pub staking_account_id: Option<AccountId>,
@@ -152,7 +154,7 @@ where
 {
     /// Increases proposal constitutionality level.
     pub fn increase_constitutionality_level(&mut self) {
-        self.current_constitutionality_level += 1;
+        self.nr_of_council_confirmations += 1;
     }
 
     /// Returns whether voting period expired by now
@@ -359,8 +361,7 @@ where
 
     // Council approved the proposal enough times.
     pub fn is_constitutionality_reached_on_approval(&self) -> bool {
-        self.proposal.current_constitutionality_level + 1
-            >= self.proposal.parameters.constitutionality
+        self.proposal.nr_of_council_confirmations + 1 >= self.proposal.parameters.constitutionality
     }
 }
 
@@ -407,9 +408,6 @@ pub struct ProposalCreationParameters<BlockNumber, Balance, MemberId, AccountId>
     pub exact_execution_block: Option<BlockNumber>,
 }
 
-// Type alias for member id.
-pub(crate) type MemberId<T> = <T as membership::Trait>::MemberId;
-
 /// Balance alias for `balances` module.
 pub type BalanceOf<T> = <T as balances::Trait>::Balance;
 

+ 2 - 2
runtime-modules/service-discovery/Cargo.toml

@@ -19,11 +19,11 @@ sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com
 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'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
 minting = { package = 'pallet-token-mint', default-features = false, path = '../token-minting'}
 recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../recurring-reward'}
 common = { package = 'pallet-common', default-features = false, path = '../common'}
 staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../staking-handler'}
+membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
 
 [features]
 default = ['std']
@@ -36,4 +36,4 @@ std = [
 	'frame-system/std',
 	'sp-runtime/std',
 	'working-group/std',
-]
+]

+ 2 - 2
runtime-modules/service-discovery/src/lib.rs

@@ -51,8 +51,8 @@ pub type IPNSIdentity = Vec<u8>;
 /// HTTP Url string to a discovery service endpoint
 pub type Url = Vec<u8>;
 
-// The storage working group instance alias.
-pub(crate) type StorageWorkingGroupInstance = working_group::Instance2;
+/// The storage working group instance alias.
+pub type StorageWorkingGroupInstance = working_group::Instance2;
 
 /// Storage provider is a worker from the  working_group module.
 pub type StorageProviderId<T> = working_group::WorkerId<T>;

+ 16 - 6
runtime-modules/service-discovery/src/mock.rs

@@ -9,13 +9,10 @@ use sp_core::H256;
 use sp_runtime::{
     testing::Header,
     traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
+    DispatchResult, Perbill,
 };
 use staking_handler::{LockComparator, StakingManager};
 
-// The storage working group instance alias.
-pub type StorageWorkingGroupInstance = working_group::Instance2;
-
 mod working_group_mod {
     pub use super::StorageWorkingGroupInstance;
     pub use working_group::Event;
@@ -94,11 +91,24 @@ impl minting::Trait for Test {
     type MintId = u64;
 }
 
-impl membership::Trait for Test {
-    type Event = MetaEvent;
+impl common::Trait for Test {
     type MemberId = u64;
     type ActorId = u64;
+}
+
+impl membership::Trait for Test {
+    type Event = MetaEvent;
     type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
+}
+
+impl common::working_group::WorkingGroupIntegration<Test> for () {
+    fn ensure_worker_origin(
+        _origin: <Test as frame_system::Trait>::Origin,
+        _worker_id: &<Test as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        unimplemented!();
+    }
 }
 
 impl common::currency::GovernanceCurrency for Test {

+ 3 - 3
runtime-modules/staking-handler/Cargo.toml

@@ -10,15 +10,15 @@ frame-support = { package = 'frame-support', default-features = false, git = 'ht
 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'}
 pallet-balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
+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'}
 codec = { package = 'parity-scale-codec', version = '1.3.1', default-features = false, features = ['derive'] }
 sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-common = { package = 'pallet-common', default-features = false, path = '../common'}
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
 
 [features]
 default = ['std']
@@ -28,5 +28,5 @@ std = [
     'frame-system/std',
     'sp-arithmetic/std',
     'pallet-balances/std',
-    'membership/std',
+    'common/std',
 ]

+ 4 - 4
runtime-modules/staking-handler/src/lib.rs

@@ -60,8 +60,8 @@ pub trait StakingHandler<AccountId, Balance, MemberId> {
 /// Implementation of the StakingHandler.
 pub struct StakingManager<
     T: frame_system::Trait
-        + membership::Trait
         + pallet_balances::Trait
+        + common::Trait
         + LockComparator<<T as pallet_balances::Trait>::Balance>,
     LockId: Get<LockIdentifier>,
 > {
@@ -71,15 +71,15 @@ pub struct StakingManager<
 
 impl<
         T: frame_system::Trait
-            + membership::Trait
             + pallet_balances::Trait
+            + common::Trait
             + LockComparator<<T as pallet_balances::Trait>::Balance>,
         LockId: Get<LockIdentifier>,
     >
     StakingHandler<
         <T as frame_system::Trait>::AccountId,
         <T as pallet_balances::Trait>::Balance,
-        <T as membership::Trait>::MemberId,
+        <T as common::Trait>::MemberId,
     > for StakingManager<T, LockId>
 {
     fn lock(
@@ -157,7 +157,7 @@ impl<
 
     // Membership support for staking accounts required.
     fn is_member_staking_account(
-        _member_id: &<T as membership::Trait>::MemberId,
+        _member_id: &<T as common::Trait>::MemberId,
         _account_id: &<T as frame_system::Trait>::AccountId,
     ) -> bool {
         true

+ 16 - 3
runtime-modules/staking-handler/src/mock.rs

@@ -6,7 +6,7 @@ use sp_core::H256;
 use sp_runtime::{
     testing::Header,
     traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
+    DispatchResult, Perbill,
 };
 
 impl_outer_origin! {
@@ -69,11 +69,15 @@ impl pallet_balances::Trait for Test {
     type MaxLocks = ();
 }
 
-impl membership::Trait for Test {
-    type Event = ();
+impl common::Trait for Test {
     type MemberId = u64;
     type ActorId = u64;
+}
+
+impl membership::Trait for Test {
+    type Event = ();
     type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
 }
 
 impl LockComparator<<Test as pallet_balances::Trait>::Balance> for Test {
@@ -90,6 +94,15 @@ impl common::currency::GovernanceCurrency for Test {
     type Currency = Balances;
 }
 
+impl common::working_group::WorkingGroupIntegration<Test> for () {
+    fn ensure_worker_origin(
+        _origin: <Test as frame_system::Trait>::Origin,
+        _worker_id: &<Test as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        unimplemented!();
+    }
+}
+
 impl pallet_timestamp::Trait for Test {
     type Moment = u64;
     type OnTimestampSet = ();

+ 1 - 4
runtime-modules/staking-handler/src/test.rs

@@ -6,10 +6,7 @@ use frame_support::traits::Currency;
 pub(crate) fn increase_total_balance_issuance_using_account_id(account_id: u64, balance: u64) {
     let initial_balance = Balances::total_issuance();
     {
-        let _ = <Test as common::currency::GovernanceCurrency>::Currency::deposit_creating(
-            &account_id,
-            balance,
-        );
+        let _ = Balances::deposit_creating(&account_id, balance);
     }
     assert_eq!(Balances::total_issuance(), initial_balance + balance);
 }

+ 1 - 2
runtime-modules/storage/Cargo.toml

@@ -12,7 +12,6 @@ frame-support = { package = 'frame-support', default-features = false, git = 'ht
 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'}
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 working-group = { package = 'pallet-working-group', default-features = false, path = '../working-group'}
 common = { package = 'pallet-common', default-features = false, path = '../common'}
@@ -24,6 +23,7 @@ balances = { package = 'pallet-balances', default-features = false, git = 'https
 minting = { package = 'pallet-token-mint', default-features = false, path = '../token-minting'}
 recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../recurring-reward'}
 staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../staking-handler'}
+membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
 
 [features]
 default = ['std']
@@ -35,7 +35,6 @@ std = [
 	'frame-system/std',
 	'sp-arithmetic/std',
 	'sp-runtime/std',
-	'membership/std',
 	'pallet-timestamp/std',
 	'working-group/std',
 	'common/std',

+ 1 - 1
runtime-modules/storage/src/data_directory.rs

@@ -47,7 +47,7 @@ pub trait Trait:
     pallet_timestamp::Trait
     + frame_system::Trait
     + data_object_type_registry::Trait
-    + membership::Trait
+    + common::Trait
     + working_group::Trait<StorageWorkingGroupInstance>
 {
     /// _Data directory_ event type.

+ 3 - 4
runtime-modules/storage/src/lib.rs

@@ -7,11 +7,10 @@ pub mod data_object_type_registry;
 
 mod tests;
 
-// The storage working group instance alias.
-pub type StorageWorkingGroupInstance = working_group::Instance2;
+pub use common::MemberId;
 
-// Alias for the member id.
-pub(crate) type MemberId<T> = <T as membership::Trait>::MemberId;
+/// The storage working group instance alias.
+pub type StorageWorkingGroupInstance = working_group::Instance2;
 
 /// Storage provider is a worker from the working group module.
 pub type StorageProviderId<T> = working_group::WorkerId<T>;

+ 18 - 3
runtime-modules/storage/src/tests/mock.rs

@@ -17,6 +17,8 @@ use crate::data_object_type_registry::IsActiveDataObjectType;
 pub use crate::StorageWorkingGroupInstance;
 pub use crate::{data_directory, data_object_storage_registry, data_object_type_registry};
 use common::currency::GovernanceCurrency;
+use frame_support::sp_runtime::DispatchResult;
+
 use membership;
 
 mod working_group_mod {
@@ -274,11 +276,24 @@ impl data_object_storage_registry::Trait for Test {
     type ContentIdExists = MockContent;
 }
 
-impl membership::Trait for Test {
-    type Event = MetaEvent;
+impl common::Trait for Test {
     type MemberId = u64;
     type ActorId = u32;
+}
+
+impl membership::Trait for Test {
+    type Event = MetaEvent;
     type MembershipFee = MembershipFee;
+    type WorkingGroup = ();
+}
+
+impl common::working_group::WorkingGroupIntegration<Test> for () {
+    fn ensure_worker_origin(
+        _origin: <Test as frame_system::Trait>::Origin,
+        _worker_id: &<Test as common::Trait>::ActorId,
+    ) -> DispatchResult {
+        unimplemented!();
+    }
 }
 
 impl minting::Trait for Test {
@@ -361,7 +376,7 @@ impl ExtBuilder {
                 handle: "alice".into(),
                 avatar_uri: "".into(),
                 about: "".into(),
-                registered_at_time: 0,
+                name: "".into(),
             }],
         }
         .assimilate_storage(&mut t)

+ 9 - 4
runtime-modules/working-group/Cargo.toml

@@ -13,19 +13,25 @@ frame-system = { package = 'frame-system', default-features = false, git = 'http
 sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 common = { package = 'pallet-common', default-features = false, path = '../common'}
-membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-frame-benchmarking = { package = 'frame-benchmarking', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca', optional = true}
 staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../staking-handler'}
 
+# Benchmarking
+frame-benchmarking = { package = 'frame-benchmarking', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca', optional = true}
+membership = { package = 'pallet-membership', default-features = false, path = '../membership', 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'}
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
 
 [features]
 default = ['std']
-runtime-benchmarks = ["frame-benchmarking"]
+runtime-benchmarks = [
+	"frame-benchmarking",
+	"membership"
+]
 std = [
 	'serde',
 	'codec/std',
@@ -35,7 +41,6 @@ std = [
 	'sp-arithmetic/std',
 	'sp-std/std',
 	'common/std',
-	'membership/std',
 	'balances/std',
 	'staking-handler/std',
 ]

+ 13 - 15
runtime-modules/working-group/src/benchmarking.rs

@@ -54,9 +54,7 @@ fn add_opening_helper<T: Trait<I>, I: Instance>(
         vec![],
         *job_opening_type,
         staking_policy,
-        Some(RewardPolicy {
-            reward_per_block: One::one(),
-        }),
+        Some(One::one()),
     )
     .unwrap();
 
@@ -183,13 +181,17 @@ fn member_funded_account<T: Trait<I> + membership::Trait, I: Instance>(
 
     let _ = Balances::<T>::make_free_balance_be(&account_id, BalanceOf::<T>::max_value());
 
-    Membership::<T>::buy_membership(
-        RawOrigin::Signed(account_id.clone()).into(),
-        Some(handle),
-        None,
-        None,
-    )
-    .unwrap();
+    let params = membership::BuyMembershipParameters {
+        root_account: account_id.clone(),
+        controller_account: account_id.clone(),
+        name: None,
+        handle: Some(handle),
+        avatar_uri: None,
+        about: None,
+        referrer_id: None,
+    };
+
+    Membership::<T>::buy_membership(RawOrigin::Signed(account_id.clone()).into(), params).unwrap();
 
     let _ = Balances::<T>::make_free_balance_be(&account_id, BalanceOf::<T>::max_value());
 
@@ -870,10 +872,6 @@ benchmarks_instance! {
             leaving_unstaking_period: T::BlockNumber::max_value(),
         };
 
-        let reward_policy = RewardPolicy {
-            reward_per_block: BalanceOf::<T>::max_value(),
-        };
-
         let description = vec![0u8; i.try_into().unwrap()];
 
     }: _(
@@ -881,7 +879,7 @@ benchmarks_instance! {
             description,
             OpeningType::Regular,
             Some(stake_policy),
-            Some(reward_policy)
+            Some(BalanceOf::<T>::max_value())
         )
     verify {
         assert!(OpeningById::<T, I>::contains_key(1));

+ 7 - 18
runtime-modules/working-group/src/checks.rs

@@ -1,6 +1,6 @@
 use crate::{
-    ApplicationId, BalanceOf, Instance, MemberId, Opening, OpeningId, OpeningType, RewardPolicy,
-    StakePolicy, Trait, Worker, WorkerId,
+    ApplicationId, BalanceOf, Instance, Opening, OpeningId, OpeningType, StakePolicy, Trait,
+    Worker, WorkerId,
 };
 
 use super::Error;
@@ -148,17 +148,6 @@ pub fn ensure_worker_exists<T: Trait<I>, I: Instance>(
     Ok(worker)
 }
 
-// Check worker: verifies that origin is signed and corresponds with the membership.
-pub(crate) fn ensure_origin_signed_by_member<T: Trait<I>, I: Instance>(
-    origin: T::Origin,
-    member_id: &MemberId<T>,
-) -> Result<(), Error<T, I>> {
-    membership::Module::<T>::ensure_member_controller_account_signed(origin, member_id)
-        .map_err(|_| Error::<T, I>::InvalidMemberOrigin)?;
-
-    Ok(())
-}
-
 /// Check worker: ensures the origin contains signed account that belongs to existing worker.
 pub fn ensure_worker_signed<T: Trait<I>, I: Instance>(
     origin: T::Origin,
@@ -216,13 +205,13 @@ pub(crate) fn ensure_valid_stake_policy<T: Trait<I>, I: Instance>(
     Ok(())
 }
 
-// Check opening: verifies reward policy for the opening.
-pub(crate) fn ensure_valid_reward_policy<T: Trait<I>, I: Instance>(
-    reward_policy: &Option<RewardPolicy<BalanceOf<T>>>,
+// Check opening: verifies reward per block for the opening.
+pub(crate) fn ensure_valid_reward_per_block<T: Trait<I>, I: Instance>(
+    reward_per_block: &Option<BalanceOf<T>>,
 ) -> Result<(), DispatchError> {
-    if let Some(reward_policy) = reward_policy {
+    if let Some(reward_per_block) = reward_per_block {
         ensure!(
-            reward_policy.reward_per_block != Zero::zero(),
+            *reward_per_block != Zero::zero(),
             Error::<T, I>::CannotRewardWithZero
         )
     }

+ 17 - 9
runtime-modules/working-group/src/lib.rs

@@ -49,14 +49,16 @@ use sp_std::vec::Vec;
 
 pub use errors::Error;
 pub use types::{
-    Application, ApplicationId, ApplyOnOpeningParameters, BalanceOf, MemberId, Opening, OpeningId,
-    OpeningType, Penalty, RewardPolicy, StakeParameters, StakePolicy, Worker, WorkerId,
+    Application, ApplicationId, ApplyOnOpeningParameters, BalanceOf, Opening, OpeningId,
+    OpeningType, Penalty, StakeParameters, StakePolicy, Worker, WorkerId,
 };
 use types::{ApplicationInfo, WorkerInfo};
 
 pub use checks::{ensure_origin_is_active_leader, ensure_worker_exists, ensure_worker_signed};
 
 use common::origin::ActorOriginValidator;
+use common::MemberId;
+use frame_support::dispatch::DispatchResult;
 use staking_handler::StakingHandler;
 
 type WeightInfoWorkingGroup<T, I> = <T as Trait<I>>::WeightInfo;
@@ -91,7 +93,7 @@ pub trait WeightInfo {
 
 /// The _Group_ main _Trait_
 pub trait Trait<I: Instance = DefaultInstance>:
-    frame_system::Trait + membership::Trait + balances::Trait
+    frame_system::Trait + balances::Trait + common::Trait
 {
     /// _Administration_ event type.
     type Event: From<Event<Self, I>> + Into<<Self as frame_system::Trait>::Event>;
@@ -326,13 +328,13 @@ decl_module! {
             description: Vec<u8>,
             opening_type: OpeningType,
             stake_policy: Option<StakePolicy<T::BlockNumber, BalanceOf<T>>>,
-            reward_policy: Option<RewardPolicy<BalanceOf<T>>>
+            reward_per_block: Option<BalanceOf<T>>
         ){
             checks::ensure_origin_for_opening_type::<T, I>(origin, opening_type)?;
 
             checks::ensure_valid_stake_policy::<T, I>(&stake_policy)?;
 
-            checks::ensure_valid_reward_policy::<T, I>(&reward_policy)?;
+            checks::ensure_valid_reward_per_block::<T, I>(&reward_per_block)?;
 
             //
             // == MUTATION SAFE ==
@@ -346,7 +348,7 @@ decl_module! {
                 created: Self::current_block(),
                 description_hash: hashed_description.as_ref().to_vec(),
                 stake_policy,
-                reward_policy,
+                reward_per_block,
             };
 
             let new_opening_id = NextOpeningId::<I>::get();
@@ -509,8 +511,8 @@ decl_module! {
             // Ensuring worker actually exists
             let worker = checks::ensure_worker_exists::<T, I>(&worker_id)?;
 
-            // Ensure that origin is signed by member with given id.
-            checks::ensure_origin_signed_by_member::<T, I>(origin, &worker.member_id)?;
+            // Ensure the origin of a member with given id.
+            T::MemberOriginValidator::ensure_actor_origin(origin, worker.member_id)?;
 
             // Ensure the worker is active.
             ensure!(!worker.is_leaving(), Error::<T, I>::WorkerIsLeaving);
@@ -1110,7 +1112,7 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
                 .stake_policy
                 .as_ref()
                 .map_or(Zero::zero(), |sp| sp.leaving_unstaking_period),
-            opening.reward_policy.as_ref().map(|rp| rp.reward_per_block),
+            opening.reward_per_block,
             Self::current_block(),
         );
 
@@ -1330,3 +1332,9 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
             .collect()
     }
 }
+
+impl<T: Trait<I>, I: Instance> common::working_group::WorkingGroupIntegration<T> for Module<T, I> {
+    fn ensure_worker_origin(origin: T::Origin, worker_id: &WorkerId<T>) -> DispatchResult {
+        checks::ensure_worker_signed::<T, I>(origin, worker_id).map(|_| ())
+    }
+}

+ 24 - 38
runtime-modules/working-group/src/tests/fixtures.rs

@@ -6,11 +6,11 @@ use sp_runtime::traits::Hash;
 use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
 
 use super::hiring_workflow::HiringWorkflow;
-use super::mock::{Balances, LockId, Membership, System, Test, TestEvent, TestWorkingGroup};
+use super::mock::{Balances, LockId, System, Test, TestEvent, TestWorkingGroup};
 use crate::types::StakeParameters;
 use crate::{
     Application, ApplyOnOpeningParameters, DefaultInstance, Opening, OpeningType, Penalty,
-    RawEvent, RewardPolicy, StakePolicy, Worker,
+    RawEvent, StakePolicy, Worker,
 };
 
 pub struct EventFixture;
@@ -40,7 +40,7 @@ pub struct AddOpeningFixture {
     opening_type: OpeningType,
     starting_block: u64,
     stake_policy: Option<StakePolicy<u64, u64>>,
-    reward_policy: Option<RewardPolicy<u64>>,
+    reward_per_block: Option<u64>,
 }
 
 impl Default for AddOpeningFixture {
@@ -51,7 +51,7 @@ impl Default for AddOpeningFixture {
             opening_type: OpeningType::Regular,
             starting_block: 0,
             stake_policy: None,
-            reward_policy: None,
+            reward_per_block: None,
         }
     }
 }
@@ -78,7 +78,7 @@ impl AddOpeningFixture {
                 description_hash: expected_hash.as_ref().to_vec(),
                 opening_type: self.opening_type,
                 stake_policy: self.stake_policy.clone(),
-                reward_policy: self.reward_policy.clone(),
+                reward_per_block: self.reward_per_block.clone(),
             };
 
             assert_eq!(actual_opening, expected_opening);
@@ -94,7 +94,7 @@ impl AddOpeningFixture {
             self.description.clone(),
             self.opening_type,
             self.stake_policy.clone(),
-            self.reward_policy.clone(),
+            self.reward_per_block.clone(),
         )?;
 
         Ok(saved_opening_next_id)
@@ -125,9 +125,9 @@ impl AddOpeningFixture {
         }
     }
 
-    pub fn with_reward_policy(self, reward_policy: Option<RewardPolicy<u64>>) -> Self {
+    pub fn with_reward_per_block(self, reward_per_block: Option<u64>) -> Self {
         Self {
-            reward_policy,
+            reward_per_block,
             ..self
         }
     }
@@ -231,20 +231,6 @@ impl ApplyOnOpeningFixture {
     }
 }
 
-pub fn setup_members(count: u8) {
-    for i in 0..count {
-        let account_id: u64 = i as u64;
-        let handle: [u8; 20] = [i; 20];
-        Membership::buy_membership(
-            RawOrigin::Signed(account_id).into(),
-            Some(handle.to_vec()),
-            None,
-            None,
-        )
-        .unwrap();
-    }
-}
-
 pub struct FillOpeningFixture {
     origin: RawOrigin<u64>,
     opening_id: u64,
@@ -253,7 +239,7 @@ pub struct FillOpeningFixture {
     reward_account_id: u64,
     staking_account_id: Option<u64>,
     stake_policy: Option<StakePolicy<u64, u64>>,
-    reward_policy: Option<RewardPolicy<u64>>,
+    reward_per_block: Option<u64>,
     created_at: u64,
 }
 
@@ -269,7 +255,7 @@ impl FillOpeningFixture {
             reward_account_id: 1,
             staking_account_id: None,
             stake_policy: None,
-            reward_policy: None,
+            reward_per_block: None,
             created_at: 0,
         }
     }
@@ -296,9 +282,9 @@ impl FillOpeningFixture {
         }
     }
 
-    pub fn with_reward_policy(self, reward_policy: Option<RewardPolicy<u64>>) -> Self {
+    pub fn with_reward_per_block(self, reward_per_block: Option<u64>) -> Self {
         Self {
-            reward_policy,
+            reward_per_block,
             ..self
         }
     }
@@ -344,7 +330,7 @@ impl FillOpeningFixture {
                     .stake_policy
                     .as_ref()
                     .map_or(0, |sp| sp.leaving_unstaking_period),
-                reward_per_block: self.reward_policy.as_ref().map(|rp| rp.reward_per_block),
+                reward_per_block: self.reward_per_block,
                 missed_reward: None,
                 created_at: self.created_at,
             };
@@ -369,7 +355,7 @@ impl FillOpeningFixture {
 pub struct HireLeadFixture {
     setup_environment: bool,
     stake_policy: Option<StakePolicy<u64, u64>>,
-    reward_policy: Option<RewardPolicy<u64>>,
+    reward_per_block: Option<u64>,
 }
 
 impl Default for HireLeadFixture {
@@ -377,7 +363,7 @@ impl Default for HireLeadFixture {
         Self {
             setup_environment: true,
             stake_policy: None,
-            reward_policy: None,
+            reward_per_block: None,
         }
     }
 }
@@ -396,9 +382,9 @@ impl HireLeadFixture {
         }
     }
 
-    pub fn with_reward_policy(self, reward_policy: Option<RewardPolicy<u64>>) -> Self {
+    pub fn with_reward_per_block(self, reward_per_block: Option<u64>) -> Self {
         Self {
-            reward_policy,
+            reward_per_block,
             ..self
         }
     }
@@ -408,7 +394,7 @@ impl HireLeadFixture {
             .with_setup_environment(self.setup_environment)
             .with_opening_type(OpeningType::Leader)
             .with_stake_policy(self.stake_policy)
-            .with_reward_policy(self.reward_policy)
+            .with_reward_per_block(self.reward_per_block)
             .add_application(b"leader".to_vec())
             .execute()
             .unwrap()
@@ -419,7 +405,7 @@ impl HireLeadFixture {
             .with_setup_environment(self.setup_environment)
             .with_opening_type(OpeningType::Leader)
             .with_stake_policy(self.stake_policy)
-            .with_reward_policy(self.reward_policy)
+            .with_reward_per_block(self.reward_per_block)
             .add_application(b"leader".to_vec())
             .expect(Err(error))
             .execute();
@@ -429,7 +415,7 @@ impl HireLeadFixture {
 pub struct HireRegularWorkerFixture {
     setup_environment: bool,
     stake_policy: Option<StakePolicy<u64, u64>>,
-    reward_policy: Option<RewardPolicy<u64>>,
+    reward_per_block: Option<u64>,
 }
 
 impl Default for HireRegularWorkerFixture {
@@ -437,7 +423,7 @@ impl Default for HireRegularWorkerFixture {
         Self {
             setup_environment: true,
             stake_policy: None,
-            reward_policy: None,
+            reward_per_block: None,
         }
     }
 }
@@ -449,9 +435,9 @@ impl HireRegularWorkerFixture {
         }
     }
 
-    pub fn with_reward_policy(self, reward_policy: Option<RewardPolicy<u64>>) -> Self {
+    pub fn with_reward_per_block(self, reward_per_block: Option<u64>) -> Self {
         Self {
-            reward_policy,
+            reward_per_block,
             ..self
         }
     }
@@ -461,7 +447,7 @@ impl HireRegularWorkerFixture {
             .with_setup_environment(self.setup_environment)
             .with_opening_type(OpeningType::Regular)
             .with_stake_policy(self.stake_policy)
-            .with_reward_policy(self.reward_policy)
+            .with_reward_per_block(self.reward_per_block)
             .add_application(b"worker".to_vec())
             .execute()
             .unwrap()

+ 8 - 8
runtime-modules/working-group/src/tests/hiring_workflow.rs

@@ -2,11 +2,11 @@ use frame_support::dispatch::{DispatchError, DispatchResult};
 use frame_system::RawOrigin;
 
 use crate::tests::fixtures::{
-    setup_members, AddOpeningFixture, ApplyOnOpeningFixture, FillOpeningFixture, HireLeadFixture,
+    AddOpeningFixture, ApplyOnOpeningFixture, FillOpeningFixture, HireLeadFixture,
 };
 use crate::tests::mock::TestWorkingGroup;
 use crate::types::StakeParameters;
-use crate::{OpeningType, RewardPolicy, StakePolicy};
+use crate::{OpeningType, StakePolicy};
 
 #[derive(Clone)]
 struct HiringWorkflowApplication {
@@ -20,7 +20,7 @@ pub struct HiringWorkflow {
     opening_type: OpeningType,
     expected_result: DispatchResult,
     stake_policy: Option<StakePolicy<u64, u64>>,
-    reward_policy: Option<RewardPolicy<u64>>,
+    reward_per_block: Option<u64>,
     applications: Vec<HiringWorkflowApplication>,
     setup_environment: bool,
 }
@@ -31,7 +31,7 @@ impl Default for HiringWorkflow {
             opening_type: OpeningType::Regular,
             expected_result: Ok(()),
             stake_policy: None,
-            reward_policy: None,
+            reward_per_block: None,
             applications: Vec::new(),
             setup_environment: true,
         }
@@ -46,9 +46,9 @@ impl HiringWorkflow {
         }
     }
 
-    pub fn with_reward_policy(self, reward_policy: Option<RewardPolicy<u64>>) -> Self {
+    pub fn with_reward_per_block(self, reward_per_block: Option<u64>) -> Self {
         Self {
-            reward_policy,
+            reward_per_block,
             ..self
         }
     }
@@ -118,7 +118,7 @@ impl HiringWorkflow {
         if matches!(self.opening_type, OpeningType::Regular) {
             HireLeadFixture::default().hire_lead();
         } else {
-            setup_members(6);
+            //         setup_members(6);
         }
     }
 
@@ -151,7 +151,7 @@ impl HiringWorkflow {
         // create the opening
         let add_worker_opening_fixture = AddOpeningFixture::default()
             .with_stake_policy(self.stake_policy.clone())
-            .with_reward_policy(self.reward_policy.clone())
+            .with_reward_per_block(self.reward_per_block.clone())
             .with_opening_type(self.opening_type)
             .with_origin(origin.clone());
 

+ 7 - 8
runtime-modules/working-group/src/tests/mock.rs

@@ -21,16 +21,12 @@ mod working_group {
     pub use crate::Event;
 }
 
-mod membership_mod {
-    pub use membership::Event;
-}
-
 impl_outer_event! {
     pub enum TestEvent for Test {
         balances<T>,
         crate DefaultInstance <T>,
-        membership_mod<T>,
         frame_system<T>,
+        membership<T>,
     }
 }
 
@@ -97,11 +93,15 @@ impl balances::Trait for Test {
     type MaxLocks = ();
 }
 
-impl membership::Trait for Test {
-    type Event = TestEvent;
+impl common::Trait for Test {
     type MemberId = u64;
     type ActorId = u64;
+}
+
+impl membership::Trait for Test {
+    type Event = TestEvent;
     type MembershipFee = MembershipFee;
+    type WorkingGroup = Module<Test>;
 }
 
 impl LockComparator<<Test as balances::Trait>::Balance> for Test {
@@ -115,7 +115,6 @@ impl LockComparator<<Test as balances::Trait>::Balance> for Test {
 
 pub type Balances = balances::Module<Test>;
 pub type System = frame_system::Module<Test>;
-pub type Membership = membership::Module<Test>;
 
 parameter_types! {
     pub const RewardPeriod: u32 = 2;

+ 25 - 52
runtime-modules/working-group/src/tests/mod.rs

@@ -17,14 +17,11 @@ use crate::tests::mock::{
     STAKING_ACCOUNT_ID_FOR_ZERO_STAKE,
 };
 use crate::types::StakeParameters;
-use crate::{
-    DefaultInstance, Error, OpeningType, Penalty, RawEvent, RewardPolicy, StakePolicy, Worker,
-};
+use crate::{DefaultInstance, Error, OpeningType, Penalty, RawEvent, StakePolicy, Worker};
 use fixtures::{
-    increase_total_balance_issuance_using_account_id, setup_members, AddOpeningFixture,
-    ApplyOnOpeningFixture, EventFixture, FillOpeningFixture, HireLeadFixture,
-    HireRegularWorkerFixture, LeaveWorkerRoleFixture, TerminateWorkerRoleFixture,
-    UpdateWorkerRoleAccountFixture,
+    increase_total_balance_issuance_using_account_id, AddOpeningFixture, ApplyOnOpeningFixture,
+    EventFixture, FillOpeningFixture, HireLeadFixture, HireRegularWorkerFixture,
+    LeaveWorkerRoleFixture, TerminateWorkerRoleFixture, UpdateWorkerRoleAccountFixture,
 };
 use frame_support::dispatch::DispatchError;
 use frame_support::StorageMap;
@@ -46,9 +43,7 @@ fn add_opening_succeeded() {
                 stake_amount: 10,
                 leaving_unstaking_period: 100,
             }))
-            .with_reward_policy(Some(RewardPolicy {
-                reward_per_block: 10,
-            }));
+            .with_reward_per_block(Some(10));
 
         let opening_id = add_opening_fixture.call_and_assert(Ok(()));
 
@@ -88,10 +83,7 @@ fn add_opening_fails_with_zero_reward() {
     build_test_externalities().execute_with(|| {
         HireLeadFixture::default().hire_lead();
 
-        let add_opening_fixture =
-            AddOpeningFixture::default().with_reward_policy(Some(RewardPolicy {
-                reward_per_block: 0,
-            }));
+        let add_opening_fixture = AddOpeningFixture::default().with_reward_per_block(Some(0));
 
         add_opening_fixture.call_and_assert(Err(
             Error::<Test, DefaultInstance>::CannotRewardWithZero.into(),
@@ -159,8 +151,6 @@ fn apply_on_opening_succeeded() {
 #[test]
 fn apply_on_opening_fails_with_invalid_opening_id() {
     build_test_externalities().execute_with(|| {
-        setup_members(2);
-
         let invalid_opening_id = 22;
 
         let apply_on_opening_fixture =
@@ -217,13 +207,11 @@ fn fill_opening_succeeded() {
         let starting_block = 1;
         run_to_block(starting_block);
 
-        let reward_policy = Some(RewardPolicy {
-            reward_per_block: 10,
-        });
+        let reward_per_block = 10;
 
         let add_opening_fixture = AddOpeningFixture::default()
             .with_starting_block(starting_block)
-            .with_reward_policy(reward_policy.clone());
+            .with_reward_per_block(Some(reward_per_block.clone()));
 
         let opening_id = add_opening_fixture.call().unwrap();
 
@@ -233,7 +221,7 @@ fn fill_opening_succeeded() {
 
         let fill_opening_fixture =
             FillOpeningFixture::default_for_ids(opening_id, vec![application_id])
-                .with_reward_policy(reward_policy)
+                .with_reward_per_block(Some(reward_per_block))
                 .with_created_at(starting_block);
 
         let worker_id = fill_opening_fixture.call_and_assert(Ok(()));
@@ -510,9 +498,7 @@ fn update_worker_role_account_fails_with_invalid_origin() {
             UpdateWorkerRoleAccountFixture::default_with_ids(worker_id, 1)
                 .with_origin(RawOrigin::None);
 
-        update_worker_account_fixture.call_and_assert(Err(
-            Error::<Test, DefaultInstance>::InvalidMemberOrigin.into(),
-        ));
+        update_worker_account_fixture.call_and_assert(Err(DispatchError::Other("Bad origin")));
     });
 }
 
@@ -541,10 +527,9 @@ fn leave_worker_role_succeeds_with_paying_missed_reward() {
     build_test_externalities().execute_with(|| {
         let account_id = 1;
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
         let block_number = 4;
 
@@ -569,10 +554,9 @@ fn leave_worker_role_succeeds_with_partial_payment_of_missed_reward() {
     build_test_externalities().execute_with(|| {
         let account_id = 1;
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
         let block_number = 4;
 
@@ -699,10 +683,9 @@ fn terminate_worker_role_succeeds_with_paying_missed_reward() {
     build_test_externalities().execute_with(|| {
         let account_id = 1;
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
         let block_number = 4;
 
@@ -1942,10 +1925,9 @@ fn increase_worker_stake_fails_with_leaving_worker() {
 fn rewards_payments_are_successful() {
     build_test_externalities().execute_with(|| {
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
 
         let worker = TestWorkingGroup::worker_by_id(worker_id);
@@ -1970,10 +1952,9 @@ fn rewards_payments_are_successful() {
 fn rewards_payments_with_no_budget() {
     build_test_externalities().execute_with(|| {
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
 
         let worker = TestWorkingGroup::worker_by_id(worker_id);
@@ -2000,10 +1981,9 @@ fn rewards_payments_with_no_budget() {
 fn rewards_payments_with_insufficient_budget_and_restored_budget() {
     build_test_externalities().execute_with(|| {
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
 
         let worker = TestWorkingGroup::worker_by_id(worker_id);
@@ -2050,11 +2030,10 @@ fn rewards_payments_with_starting_block() {
         run_to_block(starting_block);
 
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
         let reward_period: u64 = RewardPeriod::get().into();
 
         let worker_id = HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
 
         let worker = TestWorkingGroup::worker_by_id(worker_id);
@@ -2112,10 +2091,9 @@ fn update_reward_account_succeeds() {
         run_to_block(1);
 
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
 
         let new_reward_account = 22;
@@ -2135,10 +2113,9 @@ fn update_reward_account_succeeds() {
 fn update_reward_account_succeeds_for_leader() {
     build_test_externalities().execute_with(|| {
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireLeadFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire_lead();
 
         let new_reward_account = 22;
@@ -2163,10 +2140,9 @@ fn update_reward_account_fails_with_invalid_origin() {
 fn update_reward_account_fails_with_invalid_origin_signed_account() {
     build_test_externalities().execute_with(|| {
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         let worker_id = HireLeadFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire_lead();
 
         let invalid_role_account = 23333;
@@ -2185,10 +2161,9 @@ fn update_reward_account_fails_with_invalid_origin_signed_account() {
 fn update_reward_account_fails_with_invalid_worker_id() {
     build_test_externalities().execute_with(|| {
         let reward_per_block = 10;
-        let reward_policy = Some(RewardPolicy { reward_per_block });
 
         HireRegularWorkerFixture::default()
-            .with_reward_policy(reward_policy)
+            .with_reward_per_block(Some(reward_per_block))
             .hire();
 
         let invalid_worker_id = 11;
@@ -2229,16 +2204,16 @@ fn update_reward_amount_succeeds() {
 
         let worker_id = HireRegularWorkerFixture::default().hire();
 
-        let reward_per_block = Some(120);
+        let reward_per_block = 120;
 
         let update_amount_fixture = UpdateRewardAmountFixture::default_for_worker_id(worker_id)
-            .with_reward_per_block(reward_per_block);
+            .with_reward_per_block(Some(reward_per_block));
 
         update_amount_fixture.call_and_assert(Ok(()));
 
         EventFixture::assert_last_crate_event(RawEvent::WorkerRewardAmountUpdated(
             worker_id,
-            reward_per_block,
+            Some(reward_per_block),
         ));
     });
 }
@@ -2247,9 +2222,7 @@ fn update_reward_amount_succeeds() {
 fn update_reward_amount_succeeds_for_leader() {
     build_test_externalities().execute_with(|| {
         let worker_id = HireLeadFixture::default()
-            .with_reward_policy(Some(RewardPolicy {
-                reward_per_block: 1000,
-            }))
+            .with_reward_per_block(Some(1000))
             .hire_lead();
 
         let update_amount_fixture = UpdateRewardAmountFixture::default_for_worker_id(worker_id)

+ 7 - 16
runtime-modules/working-group/src/types.rs

@@ -7,14 +7,13 @@ use sp_std::vec::Vec;
 use serde::{Deserialize, Serialize};
 use sp_std::marker::PhantomData;
 
+use common::{ActorId, MemberId};
+
 /// Working group job application type alias.
 pub type Application<T> = JobApplication<<T as frame_system::Trait>::AccountId, MemberId<T>>;
 
-/// Member identifier in membership::member module.
-pub type MemberId<T> = <T as membership::Trait>::MemberId;
-
 /// Type identifier for a worker role, which must be same as membership actor identifier.
-pub type WorkerId<T> = <T as membership::Trait>::ActorId;
+pub type WorkerId<T> = ActorId<T>;
 
 /// Type for an application id.
 pub type ApplicationId = u64;
@@ -30,12 +29,12 @@ pub(crate) struct ApplicationInfo<T: crate::Trait<I>, I: crate::Instance> {
 }
 
 // WorkerId - Worker - helper struct.
-pub(crate) struct WorkerInfo<T: membership::Trait + frame_system::Trait + balances::Trait> {
+pub(crate) struct WorkerInfo<T: common::Trait + frame_system::Trait + balances::Trait> {
     pub worker_id: WorkerId<T>,
     pub worker: Worker<T>,
 }
 
-impl<T: membership::Trait + frame_system::Trait + balances::Trait> From<(WorkerId<T>, Worker<T>)>
+impl<T: common::Trait + frame_system::Trait + balances::Trait> From<(WorkerId<T>, Worker<T>)>
     for WorkerInfo<T>
 {
     fn from((worker_id, worker): (WorkerId<T>, Worker<T>)) -> Self {
@@ -71,8 +70,8 @@ pub struct Opening<BlockNumber: Ord, Balance> {
     /// Stake policy for the job opening.
     pub stake_policy: Option<StakePolicy<BlockNumber, Balance>>,
 
-    /// Reward policy for the job opening.
-    pub reward_policy: Option<RewardPolicy<Balance>>,
+    /// Reward per block for the job opening.
+    pub reward_per_block: Option<Balance>,
 }
 
 /// Defines type of the opening: regular working group fellow or group leader.
@@ -198,14 +197,6 @@ impl<AccountId: Clone, MemberId: Clone, BlockNumber, Balance>
     }
 }
 
-/// Reward policy for the job opening.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]
-pub struct RewardPolicy<Balance> {
-    /// Reward per block for the worker.
-    pub reward_per_block: Balance,
-}
-
 /// Stake policy for the job opening.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]

+ 1 - 2
runtime/Cargo.toml

@@ -4,7 +4,7 @@ edition = '2018'
 name = 'joystream-node-runtime'
 # Follow convention: https://github.com/Joystream/substrate-runtime-joystream/issues/1
 # {Authoring}.{Spec}.{Impl} of the RuntimeVersion
-version = '7.8.0'
+version = '8.0.0'
 
 [dependencies]
 # Third-party dependencies
@@ -167,7 +167,6 @@ runtime-benchmarks = [
     "pallet-timestamp/runtime-benchmarks",
     "frame-benchmarking",
     "frame-system-benchmarking",
-    "pallet-offences-benchmarking",
     "pallet-session-benchmarking",
     "pallet-utility/runtime-benchmarks",
     "proposals-discussion/runtime-benchmarks",

+ 17 - 0
runtime/src/constants.rs

@@ -46,6 +46,7 @@ parameter_types! {
     pub const StorageWorkingGroupLockId: LockIdentifier = [6; 8];
     pub const ContentWorkingGroupLockId: LockIdentifier = [7; 8];
     pub const ForumGroupLockId: LockIdentifier = [8; 8];
+    pub const MembershipWorkingGroupLockId: LockIdentifier = [9; 8];
 }
 
 lazy_static! {
@@ -59,6 +60,7 @@ lazy_static! {
             CandidacyLockId::get(),
             CouncilorLockId::get(),
             VotingLockId::get(),
+            MembershipWorkingGroupLockId::get(),
         ]),
         (ContentWorkingGroupLockId::get(), [
             ForumGroupLockId::get(),
@@ -67,6 +69,7 @@ lazy_static! {
             CandidacyLockId::get(),
             CouncilorLockId::get(),
             VotingLockId::get(),
+            MembershipWorkingGroupLockId::get(),
         ]),
         (StorageWorkingGroupLockId::get(), [
             ForumGroupLockId::get(),
@@ -75,6 +78,7 @@ lazy_static! {
             CandidacyLockId::get(),
             CouncilorLockId::get(),
             VotingLockId::get(),
+            MembershipWorkingGroupLockId::get(),
         ]),
         (ProposalsLockId::get(), [
             ForumGroupLockId::get(),
@@ -83,6 +87,7 @@ lazy_static! {
             CandidacyLockId::get(),
             CouncilorLockId::get(),
             VotingLockId::get(),
+            MembershipWorkingGroupLockId::get(),
         ]),
         (CandidacyLockId::get(), [
             ForumGroupLockId::get(),
@@ -91,6 +96,7 @@ lazy_static! {
             ProposalsLockId::get(),
             CouncilorLockId::get(),
             VotingLockId::get(),
+            MembershipWorkingGroupLockId::get(),
         ]),
         (CouncilorLockId::get(), [
             ForumGroupLockId::get(),
@@ -99,6 +105,7 @@ lazy_static! {
             ProposalsLockId::get(),
             CandidacyLockId::get(),
             VotingLockId::get(),
+            MembershipWorkingGroupLockId::get(),
         ]),
         (VotingLockId::get(), [
             ForumGroupLockId::get(),
@@ -107,6 +114,16 @@ lazy_static! {
             ProposalsLockId::get(),
             CandidacyLockId::get(),
             CouncilorLockId::get(),
+            MembershipWorkingGroupLockId::get(),
+        ]),
+        (MembershipWorkingGroupLockId::get(), [
+            ForumGroupLockId::get(),
+            ContentWorkingGroupLockId::get(),
+            StorageWorkingGroupLockId::get(),
+            ProposalsLockId::get(),
+            CandidacyLockId::get(),
+            CouncilorLockId::get(),
+            MembershipWorkingGroupLockId::get(),
         ]),
     ]
     .iter()

+ 2 - 1
runtime/src/integration/proposals/council_origin_validator.rs

@@ -3,9 +3,10 @@
 use sp_std::marker::PhantomData;
 
 use common::origin::ActorOriginValidator;
+use common::MemberId;
 use proposals_engine::VotersParameters;
 
-use super::{MemberId, MembershipOriginValidator};
+use super::MembershipOriginValidator;
 
 /// Handles work with the council.
 /// Provides implementations for ActorOriginValidator and VotersParameters.

+ 1 - 3
runtime/src/integration/proposals/membership_origin_validator.rs

@@ -3,11 +3,9 @@
 use sp_std::marker::PhantomData;
 
 use common::origin::ActorOriginValidator;
+use common::MemberId;
 use frame_system::ensure_signed;
 
-/// Member of the Joystream organization
-pub type MemberId<T> = <T as membership::Trait>::MemberId;
-
 /// Default membership actor origin validator.
 pub struct MembershipOriginValidator<T> {
     marker: PhantomData<T>,

+ 1 - 1
runtime/src/integration/proposals/mod.rs

@@ -7,5 +7,5 @@ mod proposal_encoder;
 
 pub use council_elected_handler::CouncilElectedHandler;
 pub use council_origin_validator::CouncilManager;
-pub use membership_origin_validator::{MemberId, MembershipOriginValidator};
+pub use membership_origin_validator::MembershipOriginValidator;
 pub use proposal_encoder::ExtrinsicProposalEncoder;

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

@@ -22,6 +22,7 @@ macro_rules! wrap_working_group_call {
             }
             WorkingGroup::Storage => Call::StorageWorkingGroup($working_group_instance_call),
             WorkingGroup::Forum => Call::ForumWorkingGroup($working_group_instance_call),
+            WorkingGroup::Membership => Call::MembershipWorkingGroup($working_group_instance_call),
         }
     }};
 }
@@ -120,7 +121,7 @@ where
             add_opening_params.description,
             OpeningType::Leader,
             add_opening_params.stake_policy,
-            add_opening_params.reward_policy,
+            add_opening_params.reward_per_block,
         )
     }
 

+ 38 - 11
runtime/src/lib.rs

@@ -92,8 +92,8 @@ pub fn wasm_binary_unwrap() -> &'static [u8] {
 pub const VERSION: RuntimeVersion = RuntimeVersion {
     spec_name: create_runtime_str!("joystream-node"),
     impl_name: create_runtime_str!("joystream-node"),
-    authoring_version: 7,
-    spec_version: 8,
+    authoring_version: 8,
+    spec_version: 0,
     impl_version: 0,
     apis: crate::runtime_api::EXPORTED_RUNTIME_API_VERSIONS,
     transaction_version: 1,
@@ -382,8 +382,10 @@ impl pallet_im_online::Trait for Runtime {
     type Event = Event;
     type SessionDuration = SessionDuration;
     type ReportUnresponsiveness = Offences;
+    // Using the default weights until we check if we can run the benchmarks for this pallet in
+    // the reference machine in an acceptable time.
+    type WeightInfo = ();
     type UnsignedPriority = ImOnlineUnsignedPriority;
-    type WeightInfo = weights::pallet_im_online::WeightInfo;
 }
 
 parameter_types! {
@@ -621,11 +623,15 @@ impl storage::data_object_storage_registry::Trait for Runtime {
     type ContentIdExists = DataDirectory;
 }
 
-impl membership::Trait for Runtime {
-    type Event = Event;
+impl common::Trait for Runtime {
     type MemberId = MemberId;
     type ActorId = ActorId;
+}
+
+impl membership::Trait for Runtime {
+    type Event = Event;
     type MembershipFee = MembershipFee;
+    type WorkingGroup = MembershipWorkingGroup;
 }
 
 parameter_types! {
@@ -708,9 +714,16 @@ impl forum::Trait for Runtime {
 
 impl LockComparator<<Runtime as pallet_balances::Trait>::Balance> for Runtime {
     fn are_locks_conflicting(new_lock: &LockIdentifier, existing_locks: &[LockIdentifier]) -> bool {
-        existing_locks
+        let t = existing_locks
             .iter()
-            .any(|lock| !ALLOWED_LOCK_COMBINATIONS.contains(&(*new_lock, *lock)))
+            .find(|lock| !ALLOWED_LOCK_COMBINATIONS.contains(&(*new_lock, **lock)));
+
+        if t.is_some() {
+            assert_eq!(None, t);
+            true
+        } else {
+            false
+        }
     }
 }
 
@@ -718,17 +731,21 @@ impl LockComparator<<Runtime as pallet_balances::Trait>::Balance> for Runtime {
 pub type ForumWorkingGroupInstance = working_group::Instance1;
 
 // The storage working group instance alias.
-pub type StorageWorkingGroupInstance = working_group::Instance2;
+pub type StorageWorkingGroupInstance = storage::StorageWorkingGroupInstance;
 
 // The content directory working group instance alias.
 pub type ContentDirectoryWorkingGroupInstance = working_group::Instance3;
 
+// The membership working group instance alias.
+pub type MembershipWorkingGroupInstance = working_group::Instance4;
+
 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 MembershipRewardPeriod: u32 = 14400 + 40;
 }
 
 // Staking managers type aliases.
@@ -738,6 +755,8 @@ pub type ContentDirectoryWorkingGroupStakingManager =
     staking_handler::StakingManager<Runtime, ContentWorkingGroupLockId>;
 pub type StorageWorkingGroupStakingManager =
     staking_handler::StakingManager<Runtime, StorageWorkingGroupLockId>;
+pub type MembershipWorkingGroupStakingManager =
+    staking_handler::StakingManager<Runtime, MembershipWorkingGroupLockId>;
 
 impl working_group::Trait<ForumWorkingGroupInstance> for Runtime {
     type Event = Event;
@@ -769,6 +788,16 @@ impl working_group::Trait<ContentDirectoryWorkingGroupInstance> for Runtime {
     type WeightInfo = weights::working_group::WeightInfo;
 }
 
+impl working_group::Trait<MembershipWorkingGroupInstance> for Runtime {
+    type Event = Event;
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = MembershipWorkingGroupStakingManager;
+    type MemberOriginValidator = MembershipOriginValidator<Self>;
+    type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
+    type RewardPeriod = MembershipRewardPeriod;
+    type WeightInfo = weights::working_group::WeightInfo;
+}
+
 impl service_discovery::Trait for Runtime {
     type Event = Event;
 }
@@ -819,13 +848,10 @@ impl proposals_discussion::Trait for Runtime {
 }
 
 parameter_types! {
-    pub const TextProposalMaxLength: u32 = 5_000;
     pub const RuntimeUpgradeWasmProposalMaxLength: u32 = 3_000_000;
 }
 
 impl proposals_codex::Trait for Runtime {
-    type TextProposalMaxLength = TextProposalMaxLength;
-    type RuntimeUpgradeWasmProposalMaxLength = RuntimeUpgradeWasmProposalMaxLength;
     type MembershipOriginValidator = MembershipOriginValidator<Self>;
     type ProposalEncoder = ExtrinsicProposalEncoder;
     type SetValidatorCountProposalParameters = SetValidatorCountProposalParameters;
@@ -930,5 +956,6 @@ construct_runtime!(
         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>},
+        MembershipWorkingGroup: working_group::<Instance4>::{Module, Call, Storage, Event<T>},
     }
 );

+ 27 - 3
runtime/src/runtime_api.rs

@@ -256,13 +256,23 @@ impl_runtime_apis! {
         ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {
             use sp_std::vec;
             use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};
-            use frame_system_benchmarking::Module as SystemBench;
-            impl frame_system_benchmarking::Trait for Runtime {}
 
+            use pallet_session_benchmarking::Module as SessionBench;
+            use frame_system_benchmarking::Module as SystemBench;
             use crate::ProposalsDiscussion;
             use crate::ProposalsEngine;
             use crate::Constitution;
             use crate::ContentDirectoryWorkingGroup;
+            use crate::Utility;
+            use crate::Timestamp;
+            use crate::ImOnline;
+
+
+            // Trying to add benchmarks directly to the Session Pallet caused cyclic dependency issues.
+            // To get around that, we separated the Session benchmarks into its own crate, which is why
+            // we need these two lines below.
+            impl pallet_session_benchmarking::Trait for Runtime {}
+            impl frame_system_benchmarking::Trait for Runtime {}
 
             let whitelist: Vec<TrackedStorageKey> = vec![
                 // Block Number
@@ -284,7 +294,21 @@ impl_runtime_apis! {
             let mut batches = Vec::<BenchmarkBatch>::new();
             let params = (&config, &whitelist);
 
-            add_benchmark!(params, batches, system, SystemBench::<Runtime>);
+            // Note: For benchmarking Stake and Balances we need to change ExistentialDeposit to
+            // a non-zero value.
+            // For now, due to the complexity grandpa and babe aren't benchmarked automatically
+            // we should use the default manually created weights.
+            // Finally, pallet_offences have no `WeightInfo` so there's no need to benchmark it
+            // the benchmark is only for illustrative pourpuses.
+
+            // Frame benchmarks
+            add_benchmark!(params, batches, frame_system, SystemBench::<Runtime>);
+            add_benchmark!(params, batches, pallet_utility, Utility);
+            add_benchmark!(params, batches, pallet_timestamp, Timestamp);
+            add_benchmark!(params, batches, pallet_session, SessionBench::<Runtime>);
+            add_benchmark!(params, batches, pallet_im_online, ImOnline);
+
+            // Joystream Benchmarks
             add_benchmark!(params, batches, proposals_discussion, ProposalsDiscussion);
             add_benchmark!(params, batches, proposals_engine, ProposalsEngine);
             add_benchmark!(params, batches, pallet_constitution, Constitution);

+ 12 - 7
runtime/src/tests/mod.rs

@@ -34,13 +34,18 @@ pub(crate) fn insert_member(account_id: AccountId32) {
         crate::MembershipFee::get(),
     );
     let handle: &[u8] = account_id.as_ref();
-    Membership::buy_membership(
-        RawOrigin::Signed(account_id.clone()).into(),
-        Some(handle.to_vec()),
-        None,
-        None,
-    )
-    .unwrap();
+
+    let params = membership::BuyMembershipParameters {
+        root_account: account_id.clone(),
+        controller_account: account_id.clone(),
+        name: None,
+        handle: Some(handle.to_vec()),
+        avatar_uri: None,
+        about: None,
+        referrer_id: None,
+    };
+
+    Membership::buy_membership(RawOrigin::Signed(account_id.clone()).into(), params).unwrap();
 }
 
 pub(crate) fn increase_total_balance_issuance_using_account_id(

+ 46 - 31
runtime/src/tests/proposals_integration/mod.rs

@@ -7,7 +7,7 @@ mod working_group_proposals;
 use crate::{BlockNumber, ProposalCancellationFee, Runtime};
 use codec::Encode;
 use governance::election_params::ElectionParameters;
-use membership;
+use proposals_codex::{GeneralProposalParameters, ProposalDetails};
 use proposals_engine::{
     ApprovedProposalDecision, BalanceOf, Proposal, ProposalCreationParameters, ProposalParameters,
     ProposalStatus, VoteKind, VotersParameters, VotingResults,
@@ -301,7 +301,7 @@ fn proposal_cancellation_with_slashes_with_balance_checks_succeeds() {
             status: ProposalStatus::Active,
             voting_results: VotingResults::default(),
             exact_execution_block: None,
-            current_constitutionality_level: 0,
+            nr_of_council_confirmations: 0,
             staking_account_id: Some(account_id.clone()),
         };
 
@@ -504,14 +504,18 @@ fn text_proposal_execution_succeeds() {
         let account_id: [u8; 32] = [member_id; 32];
 
         let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-            ProposalCodex::create_text_proposal(
+            let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+                member_id: member_id.into(),
+                title: b"title".to_vec(),
+                description: b"body".to_vec(),
+                staking_account_id: Some(account_id.into()),
+                exact_execution_block: None,
+            };
+
+            ProposalCodex::create_proposal(
                 RawOrigin::Signed(account_id.into()).into(),
-                member_id as u64,
-                b"title".to_vec(),
-                b"body".to_vec(),
-                Some(account_id.into()),
-                b"text".to_vec(),
-                None,
+                general_proposal_parameters,
+                ProposalDetails::Text(b"text".to_vec()),
             )
         })
         .with_member_id(member_id as u64);
@@ -532,15 +536,18 @@ fn spending_proposal_execution_succeeds() {
         assert!(Council::set_council_mint_capacity(RawOrigin::Root.into(), new_balance).is_ok());
 
         let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-            ProposalCodex::create_spending_proposal(
+            let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+                member_id: member_id.into(),
+                title: b"title".to_vec(),
+                description: b"body".to_vec(),
+                staking_account_id: Some(account_id.into()),
+                exact_execution_block: None,
+            };
+
+            ProposalCodex::create_proposal(
                 RawOrigin::Signed(account_id.clone().into()).into(),
-                member_id as u64,
-                b"title".to_vec(),
-                b"body".to_vec(),
-                Some(account_id.into()),
-                new_balance,
-                target_account_id.clone().into(),
-                None,
+                general_proposal_parameters,
+                ProposalDetails::Spending(new_balance, target_account_id.clone().into()),
             )
         })
         .with_member_id(member_id as u64);
@@ -566,14 +573,18 @@ fn set_validator_count_proposal_execution_succeeds() {
         assert_eq!(<pallet_staking::ValidatorCount>::get(), 0);
 
         let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-            ProposalCodex::create_set_validator_count_proposal(
+            let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+                member_id: member_id.into(),
+                title: b"title".to_vec(),
+                description: b"body".to_vec(),
+                staking_account_id: Some(account_id.into()),
+                exact_execution_block: None,
+            };
+
+            ProposalCodex::create_proposal(
                 RawOrigin::Signed(account_id.clone().into()).into(),
-                member_id as u64,
-                b"title".to_vec(),
-                b"body".to_vec(),
-                Some(account_id.into()),
-                new_validator_count,
-                None,
+                general_proposal_parameters,
+                ProposalDetails::SetValidatorCount(new_validator_count),
             )
         });
         codex_extrinsic_test_fixture.call_extrinsic_and_assert();
@@ -591,14 +602,18 @@ fn amend_constitution_proposal_execution_succeeds() {
         let account_id: [u8; 32] = [member_id; 32];
 
         let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-            ProposalCodex::create_amend_constitution_proposal(
+            let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+                member_id: member_id.into(),
+                title: b"title".to_vec(),
+                description: b"body".to_vec(),
+                staking_account_id: Some(account_id.into()),
+                exact_execution_block: None,
+            };
+
+            ProposalCodex::create_proposal(
                 RawOrigin::Signed(account_id.into()).into(),
-                member_id as u64,
-                b"title".to_vec(),
-                b"body".to_vec(),
-                Some(account_id.into()),
-                b"Constitution text".to_vec(),
-                None,
+                general_proposal_parameters,
+                ProposalDetails::AmendConstitution(b"Constitution text".to_vec()),
             )
         })
         .with_member_id(member_id as u64);

+ 188 - 90
runtime/src/tests/proposals_integration/working_group_proposals.rs

@@ -13,7 +13,8 @@ use crate::primitives::{ActorId, MemberId};
 use crate::{
     Balance, BlockNumber, ContentDirectoryWorkingGroup, ContentDirectoryWorkingGroupInstance,
     ContentDirectoryWorkingGroupStakingManager, ForumWorkingGroup, ForumWorkingGroupInstance,
-    ForumWorkingGroupStakingManager, StorageWorkingGroup, StorageWorkingGroupInstance,
+    ForumWorkingGroupStakingManager, MembershipWorkingGroup, MembershipWorkingGroupInstance,
+    MembershipWorkingGroupStakingManager, StorageWorkingGroup, StorageWorkingGroupInstance,
     StorageWorkingGroupStakingManager,
 };
 
@@ -54,22 +55,34 @@ fn add_opening(
             >>::contains_key(opening_id));
             opening_id
         }
+        WorkingGroup::Membership => {
+            let opening_id = MembershipWorkingGroup::next_opening_id();
+            assert!(!<working_group::OpeningById<
+                Runtime,
+                MembershipWorkingGroupInstance,
+            >>::contains_key(opening_id));
+            opening_id
+        }
     };
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_add_working_group_leader_opening_proposal(
+        let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+            member_id: member_id.into(),
+            title: b"title".to_vec(),
+            description: b"body".to_vec(),
+            staking_account_id: Some(account_id.into()),
+            exact_execution_block: None,
+        };
+
+        ProposalCodex::create_proposal(
             RawOrigin::Signed(account_id.into()).into(),
-            member_id as u64,
-            b"title".to_vec(),
-            b"body".to_vec(),
-            Some(account_id.into()),
-            AddOpeningParameters {
+            general_proposal_parameters,
+            ProposalDetails::AddWorkingGroupLeaderOpening(AddOpeningParameters {
                 description: Vec::new(),
                 stake_policy: stake_policy.clone(),
-                reward_policy: None,
+                reward_per_block: None,
                 working_group,
-            },
-            None,
+            }),
         )
     })
     .with_expected_proposal_id(expected_proposal_id)
@@ -92,18 +105,24 @@ fn fill_opening(
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_fill_working_group_leader_opening_proposal(
+        let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+            member_id: member_id.into(),
+            title: b"title".to_vec(),
+            description: b"body".to_vec(),
+            staking_account_id: Some(account_id.into()),
+            exact_execution_block: None,
+        };
+
+        ProposalCodex::create_proposal(
             RawOrigin::Signed(account_id.into()).into(),
-            member_id,
-            b"title".to_vec(),
-            b"body".to_vec(),
-            Some(account_id.into()),
-            proposals_codex::FillOpeningParameters {
-                opening_id,
-                successful_application_id,
-                working_group,
-            },
-            None,
+            general_proposal_parameters,
+            ProposalDetails::FillWorkingGroupLeaderOpening(
+                proposals_codex::FillOpeningParameters {
+                    opening_id,
+                    successful_application_id,
+                    working_group,
+                },
+            ),
         )
     })
     .disable_setup_enviroment()
@@ -125,16 +144,22 @@ fn decrease_stake(
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_decrease_working_group_leader_stake_proposal(
+        let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+            member_id: member_id.into(),
+            title: b"title".to_vec(),
+            description: b"body".to_vec(),
+            staking_account_id: Some(account_id.into()),
+            exact_execution_block: None,
+        };
+
+        ProposalCodex::create_proposal(
             RawOrigin::Signed(account_id.into()).into(),
-            member_id,
-            b"title".to_vec(),
-            b"body".to_vec(),
-            Some(account_id.into()),
-            leader_worker_id,
-            stake_amount,
-            working_group,
-            None,
+            general_proposal_parameters,
+            ProposalDetails::DecreaseWorkingGroupLeaderStake(
+                leader_worker_id,
+                stake_amount,
+                working_group,
+            ),
         )
     })
     .disable_setup_enviroment()
@@ -156,19 +181,25 @@ fn slash_stake(
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_slash_working_group_leader_stake_proposal(
+        let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+            member_id: member_id.into(),
+            title: b"title".to_vec(),
+            description: b"body".to_vec(),
+            staking_account_id: Some(account_id.into()),
+            exact_execution_block: None,
+        };
+
+        ProposalCodex::create_proposal(
             RawOrigin::Signed(account_id.into()).into(),
-            member_id,
-            b"title".to_vec(),
-            b"body".to_vec(),
-            Some(account_id.into()),
-            leader_worker_id,
-            Penalty {
-                slashing_amount: stake_amount,
-                slashing_text: Vec::new(),
-            },
-            working_group,
-            None,
+            general_proposal_parameters,
+            ProposalDetails::SlashWorkingGroupLeaderStake(
+                leader_worker_id,
+                Penalty {
+                    slashing_amount: stake_amount,
+                    slashing_text: Vec::new(),
+                },
+                working_group,
+            ),
         )
     })
     .disable_setup_enviroment()
@@ -190,16 +221,22 @@ fn set_reward(
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_set_working_group_leader_reward_proposal(
+        let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+            member_id: member_id.into(),
+            title: b"title".to_vec(),
+            description: b"body".to_vec(),
+            staking_account_id: Some(account_id.into()),
+            exact_execution_block: None,
+        };
+
+        ProposalCodex::create_proposal(
             RawOrigin::Signed(account_id.into()).into(),
-            member_id as u64,
-            b"title".to_vec(),
-            b"body".to_vec(),
-            Some(account_id.into()),
-            leader_worker_id,
-            Some(reward_amount),
-            working_group,
-            None,
+            general_proposal_parameters,
+            ProposalDetails::SetWorkingGroupLeaderReward(
+                leader_worker_id,
+                Some(reward_amount),
+                working_group,
+            ),
         )
     })
     .disable_setup_enviroment()
@@ -224,15 +261,18 @@ fn set_mint_capacity<
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_set_working_group_budget_capacity_proposal(
+        let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+            member_id: member_id.into(),
+            title: b"title".to_vec(),
+            description: b"body".to_vec(),
+            staking_account_id: Some(account_id.into()),
+            exact_execution_block: None,
+        };
+
+        ProposalCodex::create_proposal(
             RawOrigin::Signed(account_id.into()).into(),
-            member_id,
-            b"title".to_vec(),
-            b"body".to_vec(),
-            Some(account_id.into()),
-            mint_capacity,
-            working_group,
-            None,
+            general_proposal_parameters,
+            ProposalDetails::SetWorkingGroupBudgetCapacity(mint_capacity, working_group),
         )
     })
     .with_setup_enviroment(setup_environment)
@@ -254,18 +294,24 @@ fn terminate_role(
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_terminate_working_group_leader_role_proposal(
+        let general_proposal_parameters = GeneralProposalParameters::<Runtime> {
+            member_id: member_id.into(),
+            title: b"title".to_vec(),
+            description: b"body".to_vec(),
+            staking_account_id: Some(account_id.into()),
+            exact_execution_block: None,
+        };
+
+        ProposalCodex::create_proposal(
             RawOrigin::Signed(account_id.into()).into(),
-            member_id,
-            b"title".to_vec(),
-            b"body".to_vec(),
-            Some(account_id.into()),
-            proposals_codex::TerminateRoleParameters {
-                worker_id: leader_worker_id,
-                penalty: penalty.clone(),
-                working_group,
-            },
-            None,
+            general_proposal_parameters,
+            ProposalDetails::TerminateWorkingGroupLeaderRole(
+                proposals_codex::TerminateRoleParameters {
+                    worker_id: leader_worker_id,
+                    penalty: penalty.clone(),
+                    working_group,
+                },
+            ),
         )
     })
     .disable_setup_enviroment()
@@ -298,6 +344,12 @@ fn create_add_working_group_leader_opening_proposal_execution_succeeds() {
                     ForumWorkingGroupInstance,
                 >(group);
             }
+            WorkingGroup::Membership => {
+                run_create_add_working_group_leader_opening_proposal_execution_succeeds::<
+                    Runtime,
+                    MembershipWorkingGroupInstance,
+                >(group);
+            }
         }
     }
 }
@@ -308,7 +360,7 @@ fn run_create_add_working_group_leader_opening_proposal_execution_succeeds<
 >(
     working_group: WorkingGroup,
 ) where
-    <T as membership::Trait>::MemberId: From<u64>,
+    <T as common::Trait>::MemberId: From<u64>,
 {
     initial_test_ext().execute_with(|| {
         let member_id: MemberId = 1;
@@ -354,6 +406,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     ForumWorkingGroupInstance,
                 >(group);
             }
+            WorkingGroup::Membership => {
+                run_create_fill_working_group_leader_opening_proposal_execution_succeeds::<
+                    Runtime,
+                    MembershipWorkingGroupInstance,
+                >(group);
+            }
         }
     }
 }
@@ -365,8 +423,8 @@ fn run_create_fill_working_group_leader_opening_proposal_execution_succeeds<
     working_group: WorkingGroup,
 ) where
     <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-    <T as membership::Trait>::MemberId: From<u64>,
-    working_group::MemberId<T>: From<u64>,
+    <T as common::Trait>::MemberId: From<u64>,
+    common::MemberId<T>: From<u64>,
 {
     initial_test_ext().execute_with(|| {
         let member_id: u64 = 1;
@@ -435,24 +493,31 @@ fn create_decrease_group_leader_stake_proposal_execution_succeeds() {
                     ForumWorkingGroupStakingManager,
                 >(group);
             }
+            WorkingGroup::Membership => {
+                run_create_decrease_group_leader_stake_proposal_execution_succeeds::<
+                    Runtime,
+                    MembershipWorkingGroupInstance,
+                    MembershipWorkingGroupStakingManager,
+                >(group);
+            }
         }
     }
 }
 
 fn run_create_decrease_group_leader_stake_proposal_execution_succeeds<
-    T: working_group::Trait<I> + frame_system::Trait + membership::Trait + pallet_balances::Trait,
+    T: working_group::Trait<I> + frame_system::Trait + common::Trait + pallet_balances::Trait,
     I: frame_support::traits::Instance,
     SM: staking_handler::StakingHandler<
         <T as frame_system::Trait>::AccountId,
         <T as pallet_balances::Trait>::Balance,
-        <T as membership::Trait>::MemberId,
+        <T as common::Trait>::MemberId,
     >,
 >(
     working_group: WorkingGroup,
 ) where
     <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-    <T as membership::Trait>::MemberId: From<u64>,
-    <T as membership::Trait>::ActorId: Into<u64>,
+    <T as common::Trait>::MemberId: From<u64>,
+    <T as common::Trait>::ActorId: Into<u64>,
     <T as pallet_balances::Trait>::Balance: From<u128>,
 {
     initial_test_ext().execute_with(|| {
@@ -557,6 +622,13 @@ fn create_slash_group_leader_stake_proposal_execution_succeeds() {
                     ForumWorkingGroupStakingManager,
                 >(group)
             }
+            WorkingGroup::Membership => {
+                run_create_slash_group_leader_stake_proposal_execution_succeeds::<
+                    Runtime,
+                    MembershipWorkingGroupInstance,
+                    MembershipWorkingGroupStakingManager,
+                >(group)
+            }
         }
     }
 }
@@ -567,14 +639,14 @@ fn run_create_slash_group_leader_stake_proposal_execution_succeeds<
     SM: staking_handler::StakingHandler<
         <T as frame_system::Trait>::AccountId,
         <T as pallet_balances::Trait>::Balance,
-        <T as membership::Trait>::MemberId,
+        <T as common::Trait>::MemberId,
     >,
 >(
     working_group: WorkingGroup,
 ) where
     <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-    <T as membership::Trait>::MemberId: From<u64>,
-    <T as membership::Trait>::ActorId: Into<u64>,
+    <T as common::Trait>::MemberId: From<u64>,
+    <T as common::Trait>::ActorId: Into<u64>,
     <T as pallet_balances::Trait>::Balance: From<u128>,
 {
     initial_test_ext().execute_with(|| {
@@ -677,6 +749,12 @@ fn create_set_working_group_mint_capacity_proposal_execution_succeeds() {
                     ForumWorkingGroupInstance,
                 >(group);
             }
+            WorkingGroup::Membership => {
+                run_create_set_working_group_mint_capacity_proposal_execution_succeeds::<
+                    Runtime,
+                    MembershipWorkingGroupInstance,
+                >(group);
+            }
         }
     }
 }
@@ -688,7 +766,7 @@ fn run_create_set_working_group_mint_capacity_proposal_execution_succeeds<
     working_group: WorkingGroup,
 ) where
     <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-    <T as membership::Trait>::MemberId: From<u64>,
+    <T as common::Trait>::MemberId: From<u64>,
     <T as minting::Trait>::MintId: From<u64>,
     working_group::BalanceOf<T>: From<u128>,
 {
@@ -729,6 +807,12 @@ fn create_set_group_leader_reward_proposal_execution_succeeds() {
                     ForumWorkingGroupInstance,
                 >(group);
             }
+            WorkingGroup::Membership => {
+                run_create_set_working_group_mint_capacity_proposal_execution_succeeds::<
+                    Runtime,
+                    MembershipWorkingGroupInstance,
+                >(group);
+            }
         }
     }
 }
@@ -740,8 +824,8 @@ fn run_create_set_group_leader_reward_proposal_execution_succeeds<
     working_group: WorkingGroup,
 ) where
     <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-    <T as membership::Trait>::MemberId: From<u64>,
-    <T as membership::Trait>::ActorId: Into<u64>,
+    <T as common::Trait>::MemberId: From<u64>,
+    <T as common::Trait>::ActorId: Into<u64>,
     <T as minting::Trait>::MintId: From<u64>,
     working_group::BalanceOf<T>: From<u128>,
 {
@@ -830,6 +914,13 @@ fn create_terminate_group_leader_role_proposal_execution_succeeds() {
                     ForumWorkingGroupStakingManager,
                 >(group);
             }
+            WorkingGroup::Membership => {
+                run_create_terminate_group_leader_role_proposal_execution_succeeds::<
+                    Runtime,
+                    MembershipWorkingGroupInstance,
+                    MembershipWorkingGroupStakingManager,
+                >(group);
+            }
         }
     }
 }
@@ -840,15 +931,15 @@ fn run_create_terminate_group_leader_role_proposal_execution_succeeds<
     SM: staking_handler::StakingHandler<
         <T as frame_system::Trait>::AccountId,
         <T as pallet_balances::Trait>::Balance,
-        <T as membership::Trait>::MemberId,
+        <T as common::Trait>::MemberId,
     >,
 >(
     working_group: WorkingGroup,
 ) where
     <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-    <T as membership::Trait>::MemberId: From<u64>,
-    working_group::MemberId<T>: From<u64>,
-    <T as membership::Trait>::ActorId: Into<u64>,
+    <T as common::Trait>::MemberId: From<u64>,
+    common::MemberId<T>: From<u64>,
+    <T as common::Trait>::ActorId: Into<u64>,
     <T as minting::Trait>::MintId: From<u64>,
     <T as pallet_balances::Trait>::Balance: From<u128>,
 {
@@ -953,6 +1044,13 @@ fn create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds(
                     ForumWorkingGroupStakingManager,
                 >(group);
             }
+            WorkingGroup::Membership => {
+                run_create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds::<
+                    Runtime,
+                    MembershipWorkingGroupInstance,
+                    MembershipWorkingGroupStakingManager,
+                >(group);
+            }
         }
     }
 }
@@ -963,14 +1061,14 @@ fn run_create_terminate_group_leader_role_proposal_with_slashing_execution_succe
     SM: staking_handler::StakingHandler<
         <T as frame_system::Trait>::AccountId,
         <T as pallet_balances::Trait>::Balance,
-        <T as membership::Trait>::MemberId,
+        <T as common::Trait>::MemberId,
     >,
 >(
     working_group: WorkingGroup,
 ) where
     <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-    <T as membership::Trait>::MemberId: From<u64>,
-    <T as membership::Trait>::ActorId: Into<u64>,
+    <T as common::Trait>::MemberId: From<u64>,
+    <T as common::Trait>::ActorId: Into<u64>,
     <T as pallet_balances::Trait>::Balance: From<u128>,
 {
     initial_test_ext().execute_with(|| {

+ 11 - 27
runtime/src/weights/frame_system.rs

@@ -1,23 +1,7 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5
+//! 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};
 
@@ -25,33 +9,33 @@ pub struct WeightInfo;
 impl frame_system::WeightInfo for WeightInfo {
     // WARNING! Some components were not used: ["b"]
     fn remark() -> Weight {
-        (1305000 as Weight)
+        (9_342_000 as Weight)
     }
     fn set_heap_pages() -> Weight {
-        (2023000 as Weight).saturating_add(DbWeight::get().writes(1 as Weight))
+        (11_274_000 as Weight).saturating_add(DbWeight::get().writes(1 as Weight))
     }
     // WARNING! Some components were not used: ["d"]
     fn set_changes_trie_config() -> Weight {
-        (10026000 as Weight)
+        (32_325_000 as Weight)
             .saturating_add(DbWeight::get().reads(1 as Weight))
             .saturating_add(DbWeight::get().writes(2 as Weight))
     }
     fn set_storage(i: u32) -> Weight {
         (0 as Weight)
-            .saturating_add((656000 as Weight).saturating_mul(i as Weight))
+            .saturating_add((2_060_000 as Weight).saturating_mul(i as Weight))
             .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight)))
     }
     fn kill_storage(i: u32) -> Weight {
-        (4327000 as Weight)
-            .saturating_add((478000 as Weight).saturating_mul(i as Weight))
+        (27_686_000 as Weight)
+            .saturating_add((1_237_000 as Weight).saturating_mul(i as Weight))
             .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight)))
     }
     fn kill_prefix(p: u32) -> Weight {
-        (8349000 as Weight)
-            .saturating_add((838000 as Weight).saturating_mul(p as Weight))
+        (27_689_000 as Weight)
+            .saturating_add((1_205_000 as Weight).saturating_mul(p as Weight))
             .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight)))
     }
     fn suicide() -> Weight {
-        (29247000 as Weight)
+        (145_274_000 as Weight)
     }
 }

+ 2 - 1
runtime/src/weights/mod.rs

@@ -15,14 +15,15 @@
 
 //! A list of the different weight modules for our runtime.
 
+// FRAME pallets
 pub mod frame_system;
 pub mod pallet_balances;
-pub mod pallet_im_online;
 pub mod pallet_session;
 pub mod pallet_staking;
 pub mod pallet_timestamp;
 pub mod pallet_utility;
 
+// Joystream pallets
 pub mod pallet_constitution;
 pub mod proposals_discussion;
 pub mod proposals_engine;

+ 2 - 2
runtime/src/weights/pallet_constitution.rs

@@ -8,8 +8,8 @@ use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight};
 pub struct WeightInfo;
 impl pallet_constitution::WeightInfo for WeightInfo {
     fn amend_constitution(i: u32) -> Weight {
-        (145_984_000 as Weight)
-            .saturating_add((59_000 as Weight).saturating_mul(i as Weight))
+        (79_243_000 as Weight)
+            .saturating_add((64_000 as Weight).saturating_mul(i as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
 }

+ 0 - 34
runtime/src/weights/pallet_im_online.rs

@@ -1,34 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6
-
-#![allow(unused_parens)]
-#![allow(unused_imports)]
-
-use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight};
-
-pub struct WeightInfo;
-impl pallet_im_online::WeightInfo for WeightInfo {
-    fn validate_unsigned_and_then_heartbeat(k: u32, e: u32) -> Weight {
-        (139830000 as Weight)
-            .saturating_add((211000 as Weight).saturating_mul(k as Weight))
-            .saturating_add((654000 as Weight).saturating_mul(e as Weight))
-            .saturating_add(DbWeight::get().reads(4 as Weight))
-            .saturating_add(DbWeight::get().writes(1 as Weight))
-    }
-}

+ 3 - 20
runtime/src/weights/pallet_session.rs

@@ -1,21 +1,4 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
 
 #![allow(unused_parens)]
 #![allow(unused_imports)]
@@ -25,12 +8,12 @@ use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight};
 pub struct WeightInfo;
 impl pallet_session::WeightInfo for WeightInfo {
     fn set_keys() -> Weight {
-        (88_411_000 as Weight)
+        (433_479_000 as Weight)
             .saturating_add(DbWeight::get().reads(6 as Weight))
             .saturating_add(DbWeight::get().writes(5 as Weight))
     }
     fn purge_keys() -> Weight {
-        (51_843_000 as Weight)
+        (299_139_000 as Weight)
             .saturating_add(DbWeight::get().reads(2 as Weight))
             .saturating_add(DbWeight::get().writes(5 as Weight))
     }

+ 3 - 20
runtime/src/weights/pallet_utility.rs

@@ -1,21 +1,4 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
 
 #![allow(unused_parens)]
 #![allow(unused_imports)]
@@ -25,10 +8,10 @@ use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight};
 pub struct WeightInfo;
 impl pallet_utility::WeightInfo for WeightInfo {
     fn batch(c: u32) -> Weight {
-        (16461000 as Weight).saturating_add((1982000 as Weight).saturating_mul(c as Weight))
+        (0 as Weight).saturating_add((105_180_000 as Weight).saturating_mul(c as Weight))
     }
     // WARNING! Some components were not used: ["u"]
     fn as_derivative() -> Weight {
-        (4086000 as Weight)
+        (67_812_000 as Weight)
     }
 }

+ 42 - 19
scripts/generate-weights.sh

@@ -2,22 +2,45 @@
 
 # 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"
-
-echo "Benchmarking pallet_constitution..."
-./target/release/joystream-node benchmark --pallet=pallet_constitution --extrinsic=* --chain=dev --steps=50 --repeat=20 --execution=wasm --output=. > /dev/null
-mv pallet_constitution.rs runtime/src/weights/
-echo "pallet_constitution benchmarked"
-
-echo "Benchmarking working_group..."
-./target/release/joystream-node benchmark --pallet=working_group --extrinsic=* --chain=dev --steps=50 --repeat=20 --execution=wasm --output=. > /dev/null
-mv working_group.rs runtime/src/weights/
-echo "working_group benchmarked"
+SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
+
+benchmark() {
+  echo "Generating weights for $1..."
+  start=`date +%s`
+  $SCRIPT_DIR/../target/release/joystream-node benchmark \
+      --pallet=$1 \
+      --extrinsic=* \
+      --chain=dev \
+      --steps=50 \
+      --repeat=20 \
+      --execution=wasm \
+      --output=.  > /dev/null
+
+  end=`date +%s`
+
+  if [ $? -eq 0 ]; then
+      mv $SCRIPT_DIR/../*.rs $SCRIPT_DIR/../runtime/src/weights/
+      echo "Weights generated successfully for $1"
+      echo "It took $((end-start)) seconds"
+  else
+      echo "There was a problem generating the weights for $1, check the error above"
+  fi
+}
+
+# FRAME benchmarks
+benchmark frame_system
+benchmark pallet_utility
+benchmark pallet_session
+benchmark pallet_timestamp
+
+# This benchmark takes too long with 50 steps and 20 repeats in a normal laptop.
+# Will have it commented out until we test it in the reference machine. If there
+# it still takes too long we will get rid of this benchmark for good and use always
+# the default weights.
+# benchmark pallet_im_online
+
+# Joystrem benchmarks
+benchmark proposals_discussion
+benchmark proposals_engine
+benchmark pallet_constitution
+benchmark working_group

+ 1 - 1
utils/chain-spec-builder/Cargo.toml

@@ -3,7 +3,7 @@ authors = ['Joystream contributors']
 build = 'build.rs'
 edition = '2018'
 name = 'chain-spec-builder'
-version = '3.1.1'
+version = '4.0.0'
 
 [dependencies]
 enum-utils = "0.1.2"

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff