Browse Source

Merge branch 'olympia' into contitution_benchmarks

# Conflicts:
#	Cargo.lock
#	runtime/Cargo.toml
Shamil Gadelshin 4 years ago
parent
commit
bff2e86b96
51 changed files with 4003 additions and 3416 deletions
  1. 29 7
      Cargo.lock
  2. 2 1
      Cargo.toml
  3. 3 27
      node/src/chain_spec/mod.rs
  4. 0 1
      runtime-modules/membership/src/lib.rs
  5. 0 41
      runtime-modules/membership/src/staking_handler.rs
  6. 1 0
      runtime-modules/proposals/codex/Cargo.toml
  7. 27 68
      runtime-modules/proposals/codex/src/lib.rs
  8. 21 114
      runtime-modules/proposals/codex/src/proposal_types/mod.rs
  9. 11 3
      runtime-modules/proposals/codex/src/tests/mock.rs
  10. 0 98
      runtime-modules/proposals/codex/src/tests/mock/staking_handler.rs
  11. 45 89
      runtime-modules/proposals/codex/src/tests/mod.rs
  12. 3 1
      runtime-modules/proposals/engine/Cargo.toml
  13. 1 1
      runtime-modules/proposals/engine/src/lib.rs
  14. 0 1
      runtime-modules/proposals/engine/src/tests/mock/mod.rs
  15. 0 98
      runtime-modules/proposals/engine/src/tests/mock/staking_handler.rs
  16. 4 1
      runtime-modules/service-discovery/Cargo.toml
  17. 4 5
      runtime-modules/service-discovery/src/lib.rs
  18. 23 5
      runtime-modules/service-discovery/src/mock.rs
  19. 32 0
      runtime-modules/staking-handler/Cargo.toml
  20. 54 1
      runtime-modules/staking-handler/src/lib.rs
  21. 106 0
      runtime-modules/staking-handler/src/mock.rs
  22. 222 0
      runtime-modules/staking-handler/src/test.rs
  23. 2 1
      runtime-modules/storage/Cargo.toml
  24. 6 4
      runtime-modules/storage/src/data_directory.rs
  25. 5 3
      runtime-modules/storage/src/data_object_storage_registry.rs
  26. 7 5
      runtime-modules/storage/src/data_object_type_registry.rs
  27. 0 3
      runtime-modules/storage/src/lib.rs
  28. 8 3
      runtime-modules/storage/src/tests/data_object_type_registry.rs
  29. 22 12
      runtime-modules/storage/src/tests/mock.rs
  30. 16 19
      runtime-modules/working-group/Cargo.toml
  31. 1126 0
      runtime-modules/working-group/src/benchmarking.rs
  32. 260 0
      runtime-modules/working-group/src/checks.rs
  33. 45 639
      runtime-modules/working-group/src/errors.rs
  34. 421 476
      runtime-modules/working-group/src/lib.rs
  35. 609 347
      runtime-modules/working-group/src/tests/fixtures.rs
  36. 47 67
      runtime-modules/working-group/src/tests/hiring_workflow.rs
  37. 34 108
      runtime-modules/working-group/src/tests/mock.rs
  38. 422 418
      runtime-modules/working-group/src/tests/mod.rs
  39. 179 159
      runtime-modules/working-group/src/types.rs
  40. 2 0
      runtime/Cargo.toml
  41. 8 4
      runtime/src/integration/content_directory.rs
  42. 0 2
      runtime/src/integration/mod.rs
  43. 22 71
      runtime/src/integration/proposals/proposal_encoder.rs
  44. 0 93
      runtime/src/integration/working_group.rs
  45. 34 12
      runtime/src/lib.rs
  46. 3 3
      runtime/src/proposals_configuration/defaults.rs
  47. 5 5
      runtime/src/proposals_configuration/mod.rs
  48. 1 1
      runtime/src/proposals_configuration/sample_proposal_parameters.json
  49. 20 26
      runtime/src/runtime_api.rs
  50. 108 370
      runtime/src/tests/proposals_integration/working_group_proposals.rs
  51. 3 3
      runtime/src/tests/storage_integration.rs

+ 29 - 7
Cargo.lock

@@ -2427,6 +2427,7 @@ dependencies = [
  "sp-std",
  "sp-transaction-pool",
  "sp-version",
+ "staking-handler",
  "strum 0.19.5",
  "substrate-wasm-builder-runner",
 ]
@@ -4048,6 +4049,7 @@ dependencies = [
  "sp-runtime",
  "sp-staking",
  "sp-std",
+ "staking-handler",
  "strum 0.19.5",
 ]
 
@@ -4091,6 +4093,7 @@ dependencies = [
  "sp-io",
  "sp-runtime",
  "sp-std",
+ "staking-handler",
 ]
 
 [[package]]
@@ -4123,7 +4126,7 @@ dependencies = [
 
 [[package]]
 name = "pallet-service-discovery"
-version = "3.1.0"
+version = "4.0.0"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -4138,10 +4141,12 @@ dependencies = [
  "pallet-working-group",
  "parity-scale-codec",
  "serde",
+ "sp-arithmetic",
  "sp-core",
  "sp-io",
  "sp-runtime",
  "sp-std",
+ "staking-handler",
 ]
 
 [[package]]
@@ -4231,7 +4236,7 @@ dependencies = [
 
 [[package]]
 name = "pallet-storage"
-version = "3.1.0"
+version = "4.0.0"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -4251,6 +4256,7 @@ dependencies = [
  "sp-io",
  "sp-runtime",
  "sp-std",
+ "staking-handler",
 ]
 
 [[package]]
@@ -4365,18 +4371,15 @@ dependencies = [
 
 [[package]]
 name = "pallet-working-group"
-version = "3.1.0"
+version = "4.0.0"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "pallet-balances",
  "pallet-common",
- "pallet-hiring",
  "pallet-membership",
- "pallet-recurring-reward",
- "pallet-stake",
  "pallet-timestamp",
- "pallet-token-mint",
  "parity-scale-codec",
  "serde",
  "sp-arithmetic",
@@ -4384,6 +4387,7 @@ dependencies = [
  "sp-io",
  "sp-runtime",
  "sp-std",
+ "staking-handler",
 ]
 
 [[package]]
@@ -7329,6 +7333,24 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
+[[package]]
+name = "staking-handler"
+version = "1.0.0"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "pallet-common",
+ "pallet-membership",
+ "pallet-timestamp",
+ "parity-scale-codec",
+ "sp-arithmetic",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
 [[package]]
 name = "static_assertions"
 version = "1.1.0"

+ 2 - 1
Cargo.toml

@@ -18,10 +18,11 @@ members = [
 	"runtime-modules/working-group",
 	"runtime-modules/content-directory",
 	"runtime-modules/constitution",
+	"runtime-modules/staking-handler",
 	"node",
 	"utils/chain-spec-builder/"
 ]
 
 [profile.release]
 # Substrate runtime requires unwinding.
-panic = "unwind"
+panic = "unwind"

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

@@ -30,11 +30,10 @@ use sp_runtime::Perbill;
 
 use node_runtime::{
     membership, wasm_binary_unwrap, AuthorityDiscoveryConfig, BabeConfig, Balance, BalancesConfig,
-    ContentDirectoryConfig, ContentDirectoryWorkingGroupConfig, CouncilConfig,
-    CouncilElectionConfig, DataObjectStorageRegistryConfig, DataObjectTypeRegistryConfig,
-    ElectionParameters, ForumConfig, ForumWorkingGroupConfig, GrandpaConfig, ImOnlineConfig,
+    ContentDirectoryConfig, CouncilConfig, CouncilElectionConfig, DataObjectStorageRegistryConfig,
+    DataObjectTypeRegistryConfig, ElectionParameters, ForumConfig, GrandpaConfig, ImOnlineConfig,
     MembersConfig, Moment, SessionConfig, SessionKeys, Signature, StakerStatus, StakingConfig,
-    StorageWorkingGroupConfig, SudoConfig, SystemConfig, DAYS,
+    SudoConfig, SystemConfig, DAYS,
 };
 
 // Exported to be used by chain-spec-builder
@@ -214,8 +213,6 @@ pub fn testnet_genesis(
     const STASH: Balance = 5_000;
     const ENDOWMENT: Balance = 100_000_000;
 
-    let default_text_constraint = node_runtime::working_group::default_text_constraint();
-
     GenesisConfig {
         frame_system: Some(SystemConfig {
             code: wasm_binary_unwrap().to_vec(),
@@ -295,27 +292,6 @@ pub fn testnet_genesis(
         data_object_storage_registry: Some(DataObjectStorageRegistryConfig {
             first_relationship_id: 1,
         }),
-        working_group_Instance1: Some(ForumWorkingGroupConfig {
-            phantom: Default::default(),
-            working_group_mint_capacity: 0,
-            opening_human_readable_text_constraint: default_text_constraint,
-            worker_application_human_readable_text_constraint: default_text_constraint,
-            worker_exit_rationale_text_constraint: default_text_constraint,
-        }),
-        working_group_Instance2: Some(StorageWorkingGroupConfig {
-            phantom: Default::default(),
-            working_group_mint_capacity: 0,
-            opening_human_readable_text_constraint: default_text_constraint,
-            worker_application_human_readable_text_constraint: default_text_constraint,
-            worker_exit_rationale_text_constraint: default_text_constraint,
-        }),
-        working_group_Instance3: Some(ContentDirectoryWorkingGroupConfig {
-            phantom: Default::default(),
-            working_group_mint_capacity: 0,
-            opening_human_readable_text_constraint: default_text_constraint,
-            worker_application_human_readable_text_constraint: default_text_constraint,
-            worker_exit_rationale_text_constraint: default_text_constraint,
-        }),
         content_directory: Some({
             ContentDirectoryConfig {
                 class_by_id: vec![],

+ 0 - 1
runtime-modules/membership/src/lib.rs

@@ -7,7 +7,6 @@
 
 pub mod genesis;
 pub(crate) mod mock;
-pub mod staking_handler;
 mod tests;
 
 use codec::{Codec, Decode, Encode};

+ 0 - 41
runtime-modules/membership/src/staking_handler.rs

@@ -1,41 +0,0 @@
-use frame_support::dispatch::DispatchResult;
-
-// Type alias for member id.
-pub type MemberId<T> = <T as crate::Trait>::MemberId;
-
-/// Balance alias for `balances` module.
-pub type BalanceOf<T> = <T as balances::Trait>::Balance;
-
-/// Defines abstract staking handler to manage user stakes for different activities
-/// like adding a proposal. Implementation should use built-in LockableCurrency
-/// and LockIdentifier to lock balance consistently with pallet_staking.
-pub trait StakingHandler<T: frame_system::Trait + crate::Trait + balances::Trait> {
-    /// Locks the specified balance on the account using specific lock identifier.
-    fn lock(account_id: &T::AccountId, amount: BalanceOf<T>);
-
-    /// Removes the specified lock on the account.
-    fn unlock(account_id: &T::AccountId);
-
-    /// Slash the specified balance on the account using specific lock identifier.
-    /// No limits, no actions on zero stake.
-    /// If slashing balance greater than the existing stake - stake is slashed to zero.
-    /// Returns actually slashed balance.
-    fn slash(account_id: &T::AccountId, amount: Option<BalanceOf<T>>) -> BalanceOf<T>;
-
-    /// Sets the new stake to a given amount.
-    fn set_stake(account_id: &T::AccountId, new_stake: BalanceOf<T>) -> DispatchResult;
-
-    /// Verifies that staking account bound to the member.
-    fn is_member_staking_account(member_id: &MemberId<T>, account_id: &T::AccountId) -> bool;
-
-    /// Verifies that there no conflicting stakes on the staking account.
-    fn is_account_free_of_conflicting_stakes(account_id: &T::AccountId) -> bool;
-
-    /// Verifies that staking account balance is sufficient for staking.
-    /// During the balance check we should consider already locked stake. Effective balance to check
-    /// is 'already locked funds' + 'usable funds'.
-    fn is_enough_balance_for_stake(account_id: &T::AccountId, amount: BalanceOf<T>) -> bool;
-
-    /// Returns the current stake on the account.
-    fn current_stake(account_id: &T::AccountId) -> BalanceOf<T>;
-}

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

@@ -33,6 +33,7 @@ sp-staking = { package = 'sp-staking', default-features = false, git = 'https://
 pallet-staking-reward-curve = { package = 'pallet-staking-reward-curve', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 recurring-rewards = { package = 'pallet-recurring-reward', default-features = false, path = '../../recurring-reward'}
 strum = {version = "0.19", default-features = false}
+staking-handler = { package = 'staking-handler', default-features = false, path = '../../staking-handler'}
 
 [features]
 default = ['std']

+ 27 - 68
runtime-modules/proposals/codex/src/lib.rs

@@ -25,7 +25,7 @@
 //! - [create_add_working_group_leader_opening_proposal](./struct.Module.html#method.create_add_working_group_leader_opening_proposal)
 //! - [create_begin_review_working_group_leader_applications_proposal](./struct.Module.html#method.create_begin_review_working_group_leader_applications_proposal)
 //! - [create_fill_working_group_leader_opening_proposal](./struct.Module.html#method.create_fill_working_group_leader_opening_proposal)
-//! - [create_set_working_group_mint_capacity_proposal](./struct.Module.html#method.create_set_working_group_mint_capacity_proposal)
+//! - [create_set_working_group_budget_capacity_proposal](./struct.Module.html#method.create_set_working_group_budget_capacity_proposal)
 //! - [create_decrease_working_group_leader_stake_proposal](./struct.Module.html#method.create_decrease_working_group_leader_stake_proposal)
 //! - [create_slash_working_group_leader_stake_proposal](./struct.Module.html#method.create_slash_working_group_leader_stake_proposal)
 //! - [create_set_working_group_leader_reward_proposal](./struct.Module.html#method.create_set_working_group_leader_reward_proposal)
@@ -52,9 +52,6 @@
 // Disable this lint warning because Substrate generates function without an alias for the ProposalDetailsOf type.
 #![allow(clippy::too_many_arguments)]
 
-// Do not delete! Cannot be uncommented by default, because of Parity decl_module! issue.
-// #![warn(missing_docs)]
-
 mod proposal_types;
 
 #[cfg(test)]
@@ -69,21 +66,20 @@ use sp_std::clone::Clone;
 use sp_std::str::from_utf8;
 use sp_std::vec::Vec;
 
+pub use crate::proposal_types::{
+    AddOpeningParameters, FillOpeningParameters, TerminateRoleParameters,
+};
 use common::origin::ActorOriginValidator;
 use common::working_group::WorkingGroup;
-use governance::election_params::ElectionParameters;
+pub use proposal_types::{ProposalDetails, ProposalDetailsOf, ProposalEncoder};
 use proposals_discussion::ThreadMode;
 use proposals_engine::{
     BalanceOf, ProposalCreationParameters, ProposalObserver, ProposalParameters,
 };
+use working_group::Penalty;
 
-pub use crate::proposal_types::{
-    AddOpeningParameters, FillOpeningParameters, TerminateRoleParameters,
-};
-pub use proposal_types::{ProposalDetails, ProposalDetailsOf, ProposalEncoder};
-
-// 'Set working group mint capacity' proposal limit
-const WORKING_GROUP_MINT_CAPACITY_MAX_VALUE: u32 = 5_000_000;
+// 'Set working group budget capacity' proposal limit
+const WORKING_GROUP_BUDGET_CAPACITY_MAX_VALUE: u32 = 5_000_000;
 // Max allowed value for 'spending' proposal
 const MAX_SPENDING_PROPOSAL_VALUE: u32 = 5_000_000_u32;
 // Max validator count for the 'set validator count' proposal
@@ -160,8 +156,8 @@ pub trait Trait:
         ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
     >;
 
-    /// 'Set working group mint capacity' proposal parameters.
-    type SetWorkingGroupMintCapacityProposalParameters: Get<
+    /// 'Set working group budget capacity' proposal parameters.
+    type SetWorkingGroupBudgetCapacityProposalParameters: Get<
         ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
     >;
 
@@ -251,11 +247,8 @@ decl_error! {
         /// Invalid council election parameter - announcing_period
         InvalidCouncilElectionParameterAnnouncingPeriod,
 
-        /// Invalid content working group mint capacity parameter
-        InvalidContentWorkingGroupMintCapacity,
-
-        /// Invalid working group mint capacity parameter
-        InvalidWorkingGroupMintCapacity,
+        /// Invalid working group budget capacity parameter
+        InvalidWorkingGroupBudgetCapacity,
 
         /// Invalid 'set lead proposal' parameter - proposed lead cannot be a councilor
         InvalidSetLeadParameterCannotBeCouncilor,
@@ -314,9 +307,9 @@ decl_module! {
         const FillWorkingGroupOpeningProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
             = T::FillWorkingGroupOpeningProposalParameters::get();
 
-        /// Exports 'Set working group mint capacity' proposal parameters.
-        const SetWorkingGroupMintCapacityProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
-            = T::SetWorkingGroupMintCapacityProposalParameters::get();
+        /// Exports 'Set working group budget capacity' proposal parameters.
+        const SetWorkingGroupBudgetCapacityProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
+            = T::SetWorkingGroupBudgetCapacityProposalParameters::get();
 
         /// Exports 'Decrease working group leader stake' proposal parameters.
         const DecreaseWorkingGroupLeaderStakeProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
@@ -508,35 +501,6 @@ decl_module! {
             Self::create_proposal(params)?;
         }
 
-        /// Create 'Begin review working group leader applications' proposal type.
-        /// This proposal uses `begin_applicant_review()` extrinsic from the Joystream `working group` module.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_begin_review_working_group_leader_applications_proposal(
-            origin,
-            member_id: MemberId<T>,
-            title: Vec<u8>,
-            description: Vec<u8>,
-            staking_account_id: Option<T::AccountId>,
-            opening_id: working_group::OpeningId<T>,
-            working_group: WorkingGroup,
-            exact_execution_block: Option<T::BlockNumber>,
-        ) {
-            let proposal_details = ProposalDetails::BeginReviewWorkingGroupLeaderApplications(opening_id, working_group);
-            let params = CreateProposalParameters{
-                origin,
-                member_id,
-                title,
-                description,
-                staking_account_id,
-                proposal_details: proposal_details.clone(),
-                proposal_parameters: T::BeginReviewWorkingGroupApplicationsProposalParameters::get(),
-                proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
-                exact_execution_block,
-            };
-
-            Self::create_proposal(params)?;
-        }
-
         /// 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
@@ -546,12 +510,7 @@ decl_module! {
             title: Vec<u8>,
             description: Vec<u8>,
             staking_account_id: Option<T::AccountId>,
-            fill_opening_parameters: FillOpeningParameters<
-                T::BlockNumber,
-                BalanceOfMint<T>,
-                working_group::OpeningId<T>,
-                working_group::ApplicationId<T>
-            >,
+            fill_opening_parameters: FillOpeningParameters,
             exact_execution_block: Option<T::BlockNumber>,
         ) {
             let proposal_details = ProposalDetails::FillWorkingGroupLeaderOpening(fill_opening_parameters);
@@ -570,10 +529,10 @@ decl_module! {
             Self::create_proposal(params)?;
         }
 
-        /// Create 'Set working group mint capacity' proposal type.
+        /// 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_mint_capacity_proposal(
+        pub fn create_set_working_group_budget_capacity_proposal(
             origin,
             member_id: MemberId<T>,
             title: Vec<u8>,
@@ -584,11 +543,11 @@ decl_module! {
             exact_execution_block: Option<T::BlockNumber>,
         ) {
             ensure!(
-                mint_balance <= <BalanceOfMint<T>>::from(WORKING_GROUP_MINT_CAPACITY_MAX_VALUE),
-                Error::<T>::InvalidWorkingGroupMintCapacity
+                mint_balance <= <BalanceOfMint<T>>::from(WORKING_GROUP_BUDGET_CAPACITY_MAX_VALUE),
+                Error::<T>::InvalidWorkingGroupBudgetCapacity
             );
 
-            let proposal_details = ProposalDetails::SetWorkingGroupMintCapacity(mint_balance, working_group);
+            let proposal_details = ProposalDetails::SetWorkingGroupBudgetCapacity(mint_balance, working_group);
             let params = CreateProposalParameters{
                 origin,
                 member_id,
@@ -596,7 +555,7 @@ decl_module! {
                 description,
                 staking_account_id,
                 proposal_details: proposal_details.clone(),
-                proposal_parameters: T::SetWorkingGroupMintCapacityProposalParameters::get(),
+                proposal_parameters: T::SetWorkingGroupBudgetCapacityProposalParameters::get(),
                 proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
                 exact_execution_block,
             };
@@ -651,15 +610,15 @@ decl_module! {
             description: Vec<u8>,
             staking_account_id: Option<T::AccountId>,
             worker_id: working_group::WorkerId<T>,
-            slashing_stake: BalanceOf<T>,
+            penalty: Penalty<BalanceOf<T>>,
             working_group: WorkingGroup,
             exact_execution_block: Option<T::BlockNumber>,
         ) {
-            ensure!(slashing_stake != Zero::zero(), Error::<T>::SlashingStakeIsZero);
+            ensure!(penalty.slashing_amount != Zero::zero(), Error::<T>::SlashingStakeIsZero);
 
             let proposal_details = ProposalDetails::SlashWorkingGroupLeaderStake(
                 worker_id,
-                slashing_stake,
+                penalty,
                 working_group
             );
 
@@ -688,7 +647,7 @@ decl_module! {
             description: Vec<u8>,
             staking_account_id: Option<T::AccountId>,
             worker_id: working_group::WorkerId<T>,
-            reward_amount: BalanceOfMint<T>,
+            reward_amount: Option<BalanceOfMint<T>>,
             working_group: WorkingGroup,
             exact_execution_block: Option<T::BlockNumber>,
         ) {
@@ -722,7 +681,7 @@ decl_module! {
             title: Vec<u8>,
             description: Vec<u8>,
             staking_account_id: Option<T::AccountId>,
-            terminate_role_parameters: TerminateRoleParameters<working_group::WorkerId<T>>,
+            terminate_role_parameters: TerminateRoleParameters<working_group::WorkerId<T>, BalanceOf<T>>,
             exact_execution_block: Option<T::BlockNumber>,
         ) {
             let proposal_details = ProposalDetails::TerminateWorkingGroupLeaderRole(terminate_role_parameters);

+ 21 - 114
runtime-modules/proposals/codex/src/proposal_types/mod.rs

@@ -5,9 +5,10 @@ use codec::{Decode, Encode};
 use serde::{Deserialize, Serialize};
 use sp_std::vec::Vec;
 
-use crate::ElectionParameters;
 use common::working_group::WorkingGroup;
 
+use working_group::{Penalty, RewardPolicy, StakePolicy};
+
 /// Encodes proposal using its details information.
 pub trait ProposalEncoder<T: crate::Trait> {
     /// Encodes proposal using its details information.
@@ -20,11 +21,8 @@ pub type ProposalDetailsOf<T> = ProposalDetails<
     crate::BalanceOfGovernanceCurrency<T>,
     <T as frame_system::Trait>::BlockNumber,
     <T as frame_system::Trait>::AccountId,
-    working_group::OpeningId<T>,
-    working_group::ApplicationId<T>,
     crate::BalanceOf<T>,
     working_group::WorkerId<T>,
-    crate::MemberId<T>,
 >;
 
 /// Proposal details provide voters the information required for the perceived voting.
@@ -35,11 +33,8 @@ pub enum ProposalDetails<
     CurrencyBalance,
     BlockNumber,
     AccountId,
-    OpeningId,
-    ApplicationId,
     StakeBalance,
     WorkerId,
-    MemberId,
 > {
     /// The text of the `text` proposal
     Text(Vec<u8>),
@@ -47,88 +42,45 @@ pub enum ProposalDetails<
     /// The wasm code for the `runtime upgrade` proposal
     RuntimeUpgrade(Vec<u8>),
 
-    /// ********** Deprecated.
-    /// It is kept only for backward compatibility in the Pioneer. **********
-    /// Election parameters for the `set election parameters` proposal
-    SetElectionParameters(ElectionParameters<CurrencyBalance, BlockNumber>),
-
     /// Balance and destination account for the `spending` proposal
     Spending(MintedBalance, AccountId),
 
-    /// ********** Deprecated during the Babylon release.
-    /// It is kept only for backward compatibility in the Pioneer. **********
-    /// New leader memberId and account_id for the `set lead` proposal
-    DeprecatedSetLead(Option<(MemberId, AccountId)>),
-
-    /// ********** Deprecated during the Babylon release.
-    /// It is kept only for backward compatibility in the Pioneer. **********
-    /// Balance for the `set content working group mint capacity` proposal
-    DeprecatedSetContentWorkingGroupMintCapacity(MintedBalance),
-
-    /// ********** Deprecated during the Nicaea release.
-    /// It is kept only for backward compatibility in the Pioneer. **********
-    /// AccountId for the `evict storage provider` proposal
-    DeprecatedEvictStorageProvider(AccountId),
-
     /// Validator count for the `set validator count` proposal
     SetValidatorCount(u32),
 
-    /// ********** Deprecated during the Nicaea release.
-    /// It is kept only for backward compatibility in the Pioneer. **********
-    /// Role parameters for the `set storage role parameters` proposal
-    DeprecatedSetStorageRoleParameters(RoleParameters<CurrencyBalance, BlockNumber>),
-
     /// Add opening for the working group leader position.
     AddWorkingGroupLeaderOpening(AddOpeningParameters<BlockNumber, CurrencyBalance>),
 
-    /// Begin review applications for the working group leader position.
-    BeginReviewWorkingGroupLeaderApplications(OpeningId, WorkingGroup),
-
     /// Fill opening for the working group leader position.
-    FillWorkingGroupLeaderOpening(
-        FillOpeningParameters<BlockNumber, MintedBalance, OpeningId, ApplicationId>,
-    ),
+    FillWorkingGroupLeaderOpening(FillOpeningParameters),
 
-    /// Set working group mint capacity.
-    SetWorkingGroupMintCapacity(MintedBalance, WorkingGroup),
+    /// Set working group budget capacity.
+    SetWorkingGroupBudgetCapacity(MintedBalance, WorkingGroup),
 
     /// Decrease the working group leader stake.
     DecreaseWorkingGroupLeaderStake(WorkerId, StakeBalance, WorkingGroup),
 
     /// Slash the working group leader stake.
-    SlashWorkingGroupLeaderStake(WorkerId, StakeBalance, WorkingGroup),
+    SlashWorkingGroupLeaderStake(WorkerId, Penalty<StakeBalance>, WorkingGroup),
 
     /// Set working group leader reward balance.
-    SetWorkingGroupLeaderReward(WorkerId, MintedBalance, WorkingGroup),
+    SetWorkingGroupLeaderReward(WorkerId, Option<MintedBalance>, WorkingGroup),
 
     /// Fire the working group leader with possible slashing.
-    TerminateWorkingGroupLeaderRole(TerminateRoleParameters<WorkerId>),
+    TerminateWorkingGroupLeaderRole(TerminateRoleParameters<WorkerId, StakeBalance>),
 
     /// Amend constitution.
     AmendConstitution(Vec<u8>),
 }
 
-impl<
-        MintedBalance,
-        CurrencyBalance,
-        BlockNumber,
-        AccountId,
-        OpeningId,
-        ApplicationId,
-        StakeBalance,
-        WorkerId,
-        MemberId,
-    > Default
+impl<MintedBalance, CurrencyBalance, BlockNumber, AccountId, StakeBalance, WorkerId> Default
     for ProposalDetails<
         MintedBalance,
         CurrencyBalance,
         BlockNumber,
         AccountId,
-        OpeningId,
-        ApplicationId,
         StakeBalance,
         WorkerId,
-        MemberId,
     >
 {
     fn default() -> Self {
@@ -139,15 +91,12 @@ impl<
 /// Parameters for the 'terminate the leader position' proposal.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Debug)]
-pub struct TerminateRoleParameters<WorkerId> {
+pub struct TerminateRoleParameters<WorkerId, Balance> {
     /// Leader worker id to fire.
     pub worker_id: WorkerId,
 
-    /// Terminate role rationale.
-    pub rationale: Vec<u8>,
-
-    /// Slash the leader stake on terminating.
-    pub slash: bool,
+    /// Terminate role slash penalty.
+    pub penalty: Option<Penalty<Balance>>,
 
     /// Defines working group with the open position.
     pub working_group: WorkingGroup,
@@ -156,15 +105,12 @@ pub struct TerminateRoleParameters<WorkerId> {
 /// Parameters for the 'fill opening for the leader position' proposal.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Debug)]
-pub struct FillOpeningParameters<BlockNumber, Balance, OpeningId, ApplicationId> {
+pub struct FillOpeningParameters {
     /// Finalizing opening id.
-    pub opening_id: OpeningId,
+    pub opening_id: working_group::OpeningId,
 
     /// Id of the selected application.
-    pub successful_application_id: ApplicationId,
-
-    /// Position reward policy.
-    pub reward_policy: Option<working_group::RewardPolicy<Balance, BlockNumber>>,
+    pub successful_application_id: working_group::ApplicationId,
 
     /// Defines working group with the open position.
     pub working_group: WorkingGroup,
@@ -174,54 +120,15 @@ pub struct FillOpeningParameters<BlockNumber, Balance, OpeningId, ApplicationId>
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Debug)]
 pub struct AddOpeningParameters<BlockNumber, Balance> {
-    /// Activate opening at block.
-    pub activate_at: hiring::ActivateOpeningAt<BlockNumber>,
+    /// Opening description.
+    pub description: Vec<u8>,
 
-    /// Opening conditions.
-    pub commitment: working_group::OpeningPolicyCommitment<BlockNumber, Balance>,
+    /// Stake policy for the opening.
+    pub stake_policy: Option<StakePolicy<BlockNumber, Balance>>,
 
-    /// Opening description.
-    pub human_readable_text: Vec<u8>,
+    /// Reward policy for the opening.
+    pub reward_policy: Option<RewardPolicy<Balance>>,
 
     /// Defines working group with the open position.
     pub working_group: WorkingGroup,
 }
-
-/// ********** Deprecated during the Nicaea release.
-/// It is kept only for backward compatibility in the Pioneer. **********
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug)]
-pub struct RoleParameters<Balance, BlockNumber> {
-    /// Minimum balance required to stake to enter a role.
-    pub min_stake: Balance,
-
-    /// Minimum actors to maintain - if role is unstaking
-    /// and remaining actors would be less that this value - prevent or punish for unstaking.
-    pub min_actors: u32,
-
-    /// The maximum number of spots available to fill for a role.
-    pub max_actors: u32,
-
-    /// Fixed amount of tokens paid to actors' primary account.
-    pub reward: Balance,
-
-    /// Payouts are made at this block interval.
-    pub reward_period: BlockNumber,
-
-    /// Minimum amount of time before being able to unstake.
-    pub bonding_period: BlockNumber,
-
-    /// How long tokens remain locked for after unstaking.
-    pub unbonding_period: BlockNumber,
-
-    /// Minimum period required to be in service. unbonding before this time is highly penalized
-    pub min_service_period: BlockNumber,
-
-    /// "Startup" time allowed for roles that need to sync their infrastructure
-    /// with other providers before they are considered in service and punishable for
-    /// not delivering required level of service.
-    pub startup_grace_period: BlockNumber,
-
-    /// Small fee burned to make a request to enter role.
-    pub entry_request_fee: Balance,
-}

+ 11 - 3
runtime-modules/proposals/codex/src/tests/mock/mod.rs → runtime-modules/proposals/codex/src/tests/mock.rs

@@ -16,8 +16,6 @@ use crate::{ProposalDetailsOf, ProposalEncoder, ProposalParameters};
 use proposals_engine::VotersParameters;
 use sp_runtime::testing::TestXt;
 
-mod staking_handler;
-
 impl_outer_origin! {
     pub enum Origin for Test {}
 }
@@ -220,16 +218,26 @@ pub type StorageWorkingGroupInstance = working_group::Instance2;
 
 parameter_types! {
     pub const MaxWorkerNumberLimit: u32 = 100;
+    pub const LockId1: [u8; 8] = [1; 8];
+    pub const LockId2: [u8; 8] = [2; 8];
 }
 
 impl working_group::Trait<ContentDirectoryWorkingGroupInstance> for Test {
     type Event = ();
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, LockId1>;
+    type MemberOriginValidator = ();
+    type MinUnstakingPeriodLimit = ();
+    type RewardPeriod = ();
 }
 
 impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = ();
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, LockId2>;
+    type MemberOriginValidator = ();
+    type MinUnstakingPeriodLimit = ();
+    type RewardPeriod = ();
 }
 
 impl recurring_rewards::Trait for Test {
@@ -338,7 +346,7 @@ impl crate::Trait for Test {
     type AddWorkingGroupOpeningProposalParameters = DefaultProposalParameters;
     type BeginReviewWorkingGroupApplicationsProposalParameters = DefaultProposalParameters;
     type FillWorkingGroupOpeningProposalParameters = DefaultProposalParameters;
-    type SetWorkingGroupMintCapacityProposalParameters = DefaultProposalParameters;
+    type SetWorkingGroupBudgetCapacityProposalParameters = DefaultProposalParameters;
     type DecreaseWorkingGroupLeaderStakeProposalParameters = DefaultProposalParameters;
     type SlashWorkingGroupLeaderStakeProposalParameters = DefaultProposalParameters;
     type SetWorkingGroupLeaderRewardProposalParameters = DefaultProposalParameters;

+ 0 - 98
runtime-modules/proposals/codex/src/tests/mock/staking_handler.rs

@@ -1,98 +0,0 @@
-use frame_support::dispatch::{DispatchError, DispatchResult};
-use frame_support::traits::{Currency, Get, LockIdentifier, LockableCurrency, WithdrawReasons};
-use membership::staking_handler::{BalanceOf, MemberId, StakingHandler};
-use sp_arithmetic::traits::Zero;
-use sp_std::marker::PhantomData;
-
-/// Implementation of the StakingHandler.
-pub struct StakingManager<
-    T: frame_system::Trait + membership::Trait + balances::Trait,
-    LockId: Get<LockIdentifier>,
-> {
-    trait_marker: PhantomData<T>,
-    lock_id_marker: PhantomData<LockId>,
-}
-
-impl<T: frame_system::Trait + membership::Trait + balances::Trait, LockId: Get<LockIdentifier>>
-    StakingHandler<T> for StakingManager<T, LockId>
-{
-    fn lock(account_id: &T::AccountId, amount: BalanceOf<T>) {
-        <balances::Module<T>>::set_lock(LockId::get(), &account_id, amount, WithdrawReasons::all())
-    }
-
-    fn unlock(account_id: &T::AccountId) {
-        T::Currency::remove_lock(LockId::get(), &account_id);
-    }
-
-    fn slash(account_id: &T::AccountId, amount: Option<BalanceOf<T>>) -> BalanceOf<T> {
-        let locks = <balances::Module<T>>::locks(&account_id);
-
-        let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
-
-        let mut actually_slashed_balance = Default::default();
-        if let Some(existing_lock) = existing_lock {
-            Self::unlock(&account_id);
-
-            let mut slashable_amount = existing_lock.amount;
-            if let Some(amount) = amount {
-                if existing_lock.amount > amount {
-                    let new_amount = existing_lock.amount - amount;
-                    Self::lock(&account_id, new_amount);
-
-                    slashable_amount = amount;
-                }
-            }
-
-            let _ = <balances::Module<T>>::slash(&account_id, slashable_amount);
-
-            actually_slashed_balance = slashable_amount
-        }
-
-        actually_slashed_balance
-    }
-
-    fn set_stake(account_id: &T::AccountId, new_stake: BalanceOf<T>) -> DispatchResult {
-        let current_stake = Self::current_stake(account_id);
-
-        //Unlock previous stake if its not zero.
-        if current_stake > Zero::zero() {
-            Self::unlock(account_id);
-        }
-
-        if !Self::is_enough_balance_for_stake(account_id, new_stake) {
-            //Restore previous stake if its not zero.
-            if current_stake > Zero::zero() {
-                Self::lock(account_id, current_stake);
-            }
-            return Err(DispatchError::Other("Not enough balance for a new stake."));
-        }
-
-        Self::lock(account_id, new_stake);
-
-        Ok(())
-    }
-
-    fn is_member_staking_account(_member_id: &MemberId<T>, _account_id: &T::AccountId) -> bool {
-        true
-    }
-
-    fn is_account_free_of_conflicting_stakes(account_id: &T::AccountId) -> bool {
-        let locks = <balances::Module<T>>::locks(&account_id);
-
-        let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
-
-        existing_lock.is_none()
-    }
-
-    fn is_enough_balance_for_stake(account_id: &T::AccountId, amount: BalanceOf<T>) -> bool {
-        <balances::Module<T>>::usable_balance(account_id) >= amount
-    }
-
-    fn current_stake(account_id: &T::AccountId) -> BalanceOf<T> {
-        let locks = <balances::Module<T>>::locks(&account_id);
-
-        let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
-
-        existing_lock.map_or(Zero::zero(), |lock| lock.amount)
-    }
-}

+ 45 - 89
runtime-modules/proposals/codex/src/tests/mod.rs

@@ -6,9 +6,7 @@ use frame_support::traits::Currency;
 use frame_system::RawOrigin;
 
 use common::working_group::WorkingGroup;
-use hiring::ActivateOpeningAt;
 use proposals_engine::ProposalParameters;
-use working_group::OpeningPolicyCommitment;
 
 use crate::*;
 use crate::{Error, ProposalDetails};
@@ -38,7 +36,7 @@ where
     empty_stake_call: EmptyStakeCall,
     successful_call: SuccessfulCall,
     proposal_parameters: ProposalParameters<u64, u64>,
-    proposal_details: ProposalDetails<u64, u64, u64, u64, u64, u64, u64, u64, u64>,
+    proposal_details: ProposalDetails<u64, u64, u64, u64, u64, u64>,
 }
 
 impl<InsufficientRightsCall, EmptyStakeCall, SuccessfulCall>
@@ -412,9 +410,9 @@ fn run_create_add_working_group_leader_opening_proposal_common_checks_succeed(
 ) {
     initial_test_ext().execute_with(|| {
         let add_opening_parameters = AddOpeningParameters {
-            activate_at: ActivateOpeningAt::CurrentBlock,
-            commitment: OpeningPolicyCommitment::default(),
-            human_readable_text: b"some text".to_vec(),
+            description: b"some text".to_vec(),
+            stake_policy: None,
+            reward_policy: None,
             working_group,
         };
 
@@ -464,69 +462,6 @@ fn run_create_add_working_group_leader_opening_proposal_common_checks_succeed(
     });
 }
 
-#[test]
-fn create_begin_review_working_group_leader_applications_proposal_common_checks_succeed() {
-    // This uses strum crate for enum iteration
-    for group in WorkingGroup::iter() {
-        run_create_begin_review_working_group_leader_applications_proposal_common_checks_succeed(
-            group,
-        );
-    }
-}
-
-fn run_create_begin_review_working_group_leader_applications_proposal_common_checks_succeed(
-    working_group: WorkingGroup,
-) {
-    initial_test_ext().execute_with(|| {
-        let opening_id = 1; // random opening id.
-
-        increase_total_balance_issuance_using_account_id(1, 500000);
-
-        let proposal_fixture = ProposalTestFixture {
-            insufficient_rights_call: || {
-                ProposalCodex::create_begin_review_working_group_leader_applications_proposal(
-                    RawOrigin::None.into(),
-                    1,
-                    b"title".to_vec(),
-                    b"body".to_vec(),
-                    None,
-                    opening_id,
-                    working_group,
- 					None,
-                )
-            },
-            empty_stake_call: || {
-                ProposalCodex::create_begin_review_working_group_leader_applications_proposal(
-                    RawOrigin::Signed(1).into(),
-                    1,
-                    b"title".to_vec(),
-                    b"body".to_vec(),
-                    None,
-                    opening_id,
-                    working_group,
- 					None,
-                )
-            },
-            successful_call: || {
-                ProposalCodex::create_begin_review_working_group_leader_applications_proposal(
-                    RawOrigin::Signed(1).into(),
-                    1,
-                    b"title".to_vec(),
-                    b"body".to_vec(),
-                    Some(1),
-                    opening_id,
-                    working_group,
- 					None,
-                )
-            },
-            proposal_parameters: <Test as crate::Trait>::BeginReviewWorkingGroupApplicationsProposalParameters::get(),
-            proposal_details: ProposalDetails::BeginReviewWorkingGroupLeaderApplications(opening_id,
-                working_group),
-        };
-        proposal_fixture.check_all();
-    });
-}
-
 #[test]
 fn create_fill_working_group_leader_opening_proposal_common_checks_succeed() {
     // This uses strum crate for enum iteration
@@ -544,7 +479,6 @@ fn run_create_fill_working_group_leader_opening_proposal_common_checks_succeed(
         let fill_opening_parameters = FillOpeningParameters {
             opening_id,
             successful_application_id: 1,
-            reward_policy: None,
             working_group,
         };
 
@@ -609,17 +543,17 @@ fn run_create_working_group_mint_capacity_proposal_fails_with_invalid_parameters
         increase_total_balance_issuance_using_account_id(1, 500000);
 
         assert_eq!(
-            ProposalCodex::create_set_working_group_mint_capacity_proposal(
+            ProposalCodex::create_set_working_group_budget_capacity_proposal(
                 RawOrigin::Signed(1).into(),
                 1,
                 b"title".to_vec(),
                 b"body".to_vec(),
                 Some(1),
-                (crate::WORKING_GROUP_MINT_CAPACITY_MAX_VALUE + 1) as u64,
+                (crate::WORKING_GROUP_BUDGET_CAPACITY_MAX_VALUE + 1) as u64,
                 working_group,
                 None,
             ),
-            Err(Error::<Test>::InvalidWorkingGroupMintCapacity.into())
+            Err(Error::<Test>::InvalidWorkingGroupBudgetCapacity.into())
         );
     });
 }
@@ -640,7 +574,7 @@ fn run_create_set_working_group_mint_capacity_proposal_common_checks_succeed(
 
         let proposal_fixture = ProposalTestFixture {
             insufficient_rights_call: || {
-                ProposalCodex::create_set_working_group_mint_capacity_proposal(
+                ProposalCodex::create_set_working_group_budget_capacity_proposal(
                     RawOrigin::None.into(),
                     1,
                     b"title".to_vec(),
@@ -652,7 +586,7 @@ fn run_create_set_working_group_mint_capacity_proposal_common_checks_succeed(
                 )
             },
             empty_stake_call: || {
-                ProposalCodex::create_set_working_group_mint_capacity_proposal(
+                ProposalCodex::create_set_working_group_budget_capacity_proposal(
                     RawOrigin::Signed(1).into(),
                     1,
                     b"title".to_vec(),
@@ -664,7 +598,7 @@ fn run_create_set_working_group_mint_capacity_proposal_common_checks_succeed(
                 )
             },
             successful_call: || {
-                ProposalCodex::create_set_working_group_mint_capacity_proposal(
+                ProposalCodex::create_set_working_group_budget_capacity_proposal(
                     RawOrigin::Signed(1).into(),
                     1,
                     b"title".to_vec(),
@@ -676,8 +610,8 @@ fn run_create_set_working_group_mint_capacity_proposal_common_checks_succeed(
                 )
             },
             proposal_parameters:
-                <Test as crate::Trait>::SetWorkingGroupMintCapacityProposalParameters::get(),
-            proposal_details: ProposalDetails::SetWorkingGroupMintCapacity(10, working_group),
+                <Test as crate::Trait>::SetWorkingGroupBudgetCapacityProposalParameters::get(),
+            proposal_details: ProposalDetails::SetWorkingGroupBudgetCapacity(10, working_group),
         };
         proposal_fixture.check_all();
     });
@@ -772,7 +706,10 @@ fn run_create_slash_working_group_leader_stake_proposal_common_checks_succeed(
                     b"body".to_vec(),
                     None,
                     0,
-                    10,
+                    Penalty {
+                        slashing_amount: 10,
+                        slashing_text: Vec::new(),
+                    },
                     working_group,
                     None,
                 )
@@ -785,7 +722,10 @@ fn run_create_slash_working_group_leader_stake_proposal_common_checks_succeed(
                     b"body".to_vec(),
                     None,
                     0,
-                    10,
+                    Penalty {
+                        slashing_amount: 10,
+                        slashing_text: Vec::new(),
+                    },
                     working_group,
                     None,
                 )
@@ -798,14 +738,24 @@ fn run_create_slash_working_group_leader_stake_proposal_common_checks_succeed(
                     b"body".to_vec(),
                     Some(1),
                     10,
-                    10,
+                    Penalty {
+                        slashing_amount: 10,
+                        slashing_text: Vec::new(),
+                    },
                     working_group,
                     None,
                 )
             },
             proposal_parameters:
                 <Test as crate::Trait>::SlashWorkingGroupLeaderStakeProposalParameters::get(),
-            proposal_details: ProposalDetails::SlashWorkingGroupLeaderStake(10, 10, working_group),
+            proposal_details: ProposalDetails::SlashWorkingGroupLeaderStake(
+                10,
+                Penalty {
+                    slashing_amount: 10,
+                    slashing_text: Vec::new(),
+                },
+                working_group,
+            ),
         };
         proposal_fixture.check_all();
     });
@@ -838,7 +788,10 @@ fn run_slash_stake_with_zero_staking_balance_fails(working_group: WorkingGroup)
                 b"body".to_vec(),
                 Some(1),
                 10,
-                0,
+                Penalty {
+                    slashing_amount: 0,
+                    slashing_text: Vec::new()
+                },
                 working_group,
                 None,
             ),
@@ -904,7 +857,7 @@ fn run_create_set_working_group_leader_reward_proposal_common_checks_succeed(
                     b"body".to_vec(),
                     None,
                     0,
-                    10,
+                    Some(10),
                     working_group,
                     None,
                 )
@@ -917,7 +870,7 @@ fn run_create_set_working_group_leader_reward_proposal_common_checks_succeed(
                     b"body".to_vec(),
                     None,
                     0,
-                    10,
+                    Some(10),
                     working_group,
                     None,
                 )
@@ -930,14 +883,18 @@ fn run_create_set_working_group_leader_reward_proposal_common_checks_succeed(
                     b"body".to_vec(),
                     Some(1),
                     10,
-                    10,
+                    Some(10),
                     working_group,
                     None,
                 )
             },
             proposal_parameters:
                 <Test as crate::Trait>::SlashWorkingGroupLeaderStakeProposalParameters::get(),
-            proposal_details: ProposalDetails::SetWorkingGroupLeaderReward(10, 10, working_group),
+            proposal_details: ProposalDetails::SetWorkingGroupLeaderReward(
+                10,
+                Some(10),
+                working_group,
+            ),
         };
         proposal_fixture.check_all();
     });
@@ -959,8 +916,7 @@ fn run_create_terminate_working_group_leader_role_proposal_common_checks_succeed
 
         let terminate_role_parameters = TerminateRoleParameters {
             worker_id: 10,
-            rationale: Vec::new(),
-            slash: false,
+            penalty: None,
             working_group,
         };
 

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

@@ -16,6 +16,7 @@ sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://
 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 = 'staking-handler', default-features = false, path = '../../staking-handler'}
 
 # Benchmark dependencies.
 frame-benchmarking = { package = 'frame-benchmarking', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca', optional = true}
@@ -50,4 +51,5 @@ std = [
 	'balances/std',
     'membership/std',
     'common/std',
-]
+    'staking-handler/std',
+]

+ 1 - 1
runtime-modules/proposals/engine/src/lib.rs

@@ -137,7 +137,7 @@ use sp_arithmetic::traits::{SaturatedConversion, Saturating, Zero};
 use sp_std::vec::Vec;
 
 use common::origin::ActorOriginValidator;
-use membership::staking_handler::StakingHandler;
+use staking_handler::StakingHandler;
 
 /// Proposals engine WeightInfo.
 /// Note: This was auto generated through the benchmark CLI using the `--weight-trait` flag

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

@@ -17,7 +17,6 @@ use sp_runtime::{
 };
 
 pub(crate) mod proposals;
-pub(crate) mod staking_handler;
 
 use crate::ProposalObserver;
 pub use proposals::*;

+ 0 - 98
runtime-modules/proposals/engine/src/tests/mock/staking_handler.rs

@@ -1,98 +0,0 @@
-use frame_support::dispatch::{DispatchError, DispatchResult};
-use frame_support::traits::{Currency, Get, LockIdentifier, LockableCurrency, WithdrawReasons};
-use membership::staking_handler::{BalanceOf, MemberId, StakingHandler};
-use sp_arithmetic::traits::Zero;
-use sp_std::marker::PhantomData;
-
-/// Implementation of the StakingHandler.
-pub struct StakingManager<
-    T: frame_system::Trait + membership::Trait + balances::Trait,
-    LockId: Get<LockIdentifier>,
-> {
-    trait_marker: PhantomData<T>,
-    lock_id_marker: PhantomData<LockId>,
-}
-
-impl<T: frame_system::Trait + membership::Trait + balances::Trait, LockId: Get<LockIdentifier>>
-    StakingHandler<T> for StakingManager<T, LockId>
-{
-    fn lock(account_id: &T::AccountId, amount: BalanceOf<T>) {
-        <balances::Module<T>>::set_lock(LockId::get(), &account_id, amount, WithdrawReasons::all())
-    }
-
-    fn unlock(account_id: &T::AccountId) {
-        T::Currency::remove_lock(LockId::get(), &account_id);
-    }
-
-    fn slash(account_id: &T::AccountId, amount: Option<BalanceOf<T>>) -> BalanceOf<T> {
-        let locks = <balances::Module<T>>::locks(&account_id);
-
-        let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
-
-        let mut actually_slashed_balance = Default::default();
-        if let Some(existing_lock) = existing_lock {
-            Self::unlock(&account_id);
-
-            let mut slashable_amount = existing_lock.amount;
-            if let Some(amount) = amount {
-                if existing_lock.amount > amount {
-                    let new_amount = existing_lock.amount - amount;
-                    Self::lock(&account_id, new_amount);
-
-                    slashable_amount = amount;
-                }
-            }
-
-            let _ = <balances::Module<T>>::slash(&account_id, slashable_amount);
-
-            actually_slashed_balance = slashable_amount
-        }
-
-        actually_slashed_balance
-    }
-
-    fn set_stake(account_id: &T::AccountId, new_stake: BalanceOf<T>) -> DispatchResult {
-        let current_stake = Self::current_stake(account_id);
-
-        //Unlock previous stake if its not zero.
-        if current_stake > Zero::zero() {
-            Self::unlock(account_id);
-        }
-
-        if !Self::is_enough_balance_for_stake(account_id, new_stake) {
-            //Restore previous stake if its not zero.
-            if current_stake > Zero::zero() {
-                Self::lock(account_id, current_stake);
-            }
-            return Err(DispatchError::Other("Not enough balance for a new stake."));
-        }
-
-        Self::lock(account_id, new_stake);
-
-        Ok(())
-    }
-
-    fn is_member_staking_account(_member_id: &MemberId<T>, _account_id: &T::AccountId) -> bool {
-        true
-    }
-
-    fn is_account_free_of_conflicting_stakes(account_id: &T::AccountId) -> bool {
-        let locks = <balances::Module<T>>::locks(&account_id);
-
-        let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
-
-        existing_lock.is_none()
-    }
-
-    fn is_enough_balance_for_stake(account_id: &T::AccountId, amount: BalanceOf<T>) -> bool {
-        <balances::Module<T>>::usable_balance(account_id) >= amount
-    }
-
-    fn current_stake(account_id: &T::AccountId) -> BalanceOf<T> {
-        let locks = <balances::Module<T>>::locks(&account_id);
-
-        let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
-
-        existing_lock.map_or(Zero::zero(), |lock| lock.amount)
-    }
-}

+ 4 - 1
runtime-modules/service-discovery/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-service-discovery'
-version = '3.1.0'
+version = '4.0.0'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -8,6 +8,7 @@ edition = '2018'
 serde = { version = "1.0.101", optional = true, features = ["derive"] }
 codec = { package = 'parity-scale-codec', version = '1.3.4', default-features = false, features = ['derive'] }
 sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
@@ -24,6 +25,7 @@ hiring = { package = 'pallet-hiring', default-features = false, path = '../hirin
 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 = 'staking-handler', default-features = false, path = '../staking-handler'}
 
 [features]
 default = ['std']
@@ -31,6 +33,7 @@ std = [
 	'serde',
 	'codec/std',
 	'sp-std/std',
+	'sp-arithmetic/std',
 	'frame-support/std',
 	'frame-system/std',
 	'sp-runtime/std',

+ 4 - 5
runtime-modules/service-discovery/src/lib.rs

@@ -30,6 +30,8 @@ use frame_support::{decl_event, decl_module, decl_storage, ensure};
 use frame_system::ensure_root;
 use sp_std::vec::Vec;
 
+use working_group::ensure_worker_signed;
+
 /*
   Although there is support for ed25519 keys as the IPNS identity key and we could potentially
   reuse the same key for the role account and ipns (and make this discovery module obselete)
@@ -52,9 +54,6 @@ pub type Url = Vec<u8>;
 // The storage working group instance alias.
 pub(crate) type StorageWorkingGroupInstance = working_group::Instance2;
 
-// Alias for storage working group.
-pub(crate) type StorageWorkingGroup<T> = working_group::Module<T, StorageWorkingGroupInstance>;
-
 /// Storage provider is a worker from the  working_group module.
 pub type StorageProviderId<T> = working_group::WorkerId<T>;
 
@@ -124,7 +123,7 @@ decl_module! {
             storage_provider_id: StorageProviderId<T>,
             id: Vec<u8>,
         ) {
-            <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
+            ensure_worker_signed::<T, StorageWorkingGroupInstance>(origin, &storage_provider_id)?;
 
             // TODO: ensure id is a valid base58 encoded IPNS identity
 
@@ -144,7 +143,7 @@ decl_module! {
         /// Requires signed storage provider credentials.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn unset_ipns_id(origin, storage_provider_id: StorageProviderId<T>) {
-            <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
+            ensure_worker_signed::<T, StorageWorkingGroupInstance>(origin, &storage_provider_id)?;
 
             // == MUTATION SAFE ==
 

+ 23 - 5
runtime-modules/service-discovery/src/mock.rs

@@ -136,11 +136,24 @@ impl recurringrewards::Trait for Test {
 
 parameter_types! {
     pub const MaxWorkerNumberLimit: u32 = 3;
+    pub const LockId1: [u8; 8] = [1; 8];
 }
 
 impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = MetaEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, LockId1>;
+    type MemberOriginValidator = ();
+    type MinUnstakingPeriodLimit = ();
+    type RewardPeriod = ();
+}
+
+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 pallet_timestamp::Trait for Test {
@@ -163,14 +176,19 @@ pub type System = frame_system::Module<Test>;
 pub type Discovery = Module<Test>;
 
 pub(crate) fn hire_storage_provider() -> (u64, u64) {
-    let storage_provider_id = 1;
-    let role_account_id = 1;
+    let storage_provider_id = 1u64;
+    let role_account_id = 1u64;
 
-    let storage_provider = working_group::Worker {
+    let storage_provider = working_group::Worker::<Test> {
         member_id: 1,
         role_account_id,
-        reward_relationship: None,
-        role_stake_profile: None,
+        staking_account_id: None,
+        reward_account_id: role_account_id,
+        started_leaving_at: None,
+        job_unstaking_period: 0,
+        reward_per_block: None,
+        missed_reward: None,
+        created_at: 1,
     };
 
     <working_group::WorkerById<Test, StorageWorkingGroupInstance>>::insert(

+ 32 - 0
runtime-modules/staking-handler/Cargo.toml

@@ -0,0 +1,32 @@
+[package]
+name = 'staking-handler'
+version = '1.0.0'
+authors = ['Joystream contributors']
+edition = '2018'
+
+[dependencies]
+sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+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'}
+
+[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'}
+
+[features]
+default = ['std']
+std = [
+    'sp-std/std',
+    'frame-support/std',
+    'frame-system/std',
+    'sp-arithmetic/std',
+    'pallet-balances/std',
+    'membership/std',
+]

+ 54 - 1
runtime/src/integration/staking_handler.rs → runtime-modules/staking-handler/src/lib.rs

@@ -1,9 +1,61 @@
+//! Staking handler module.
+//! Contains StakingHandler trait and its implementation - StakingManager.
+//! StakingHandler is responsible for staking logic in the Joystream runtime:
+//! https://joystream.gitbook.io/joystream-handbook/key-concepts/stakingmock.rs
+
+// Ensure we're `no_std` when compiling for Wasm.
+#![cfg_attr(not(feature = "std"), no_std)]
+
 use frame_support::dispatch::{DispatchError, DispatchResult};
 use frame_support::traits::{Currency, Get, LockIdentifier, LockableCurrency, WithdrawReasons};
-use membership::staking_handler::{BalanceOf, MemberId, StakingHandler};
 use sp_arithmetic::traits::Zero;
 use sp_std::marker::PhantomData;
 
+#[cfg(test)]
+mod mock;
+#[cfg(test)]
+mod test;
+
+/// Type alias for member id.
+pub type MemberId<T> = <T as membership::Trait>::MemberId;
+
+/// Balance alias for `balances` module.
+pub type BalanceOf<T> = <T as pallet_balances::Trait>::Balance;
+
+/// Defines abstract staking handler to manage user stakes for different activities
+/// like adding a proposal. Implementation should use built-in LockableCurrency
+/// and LockIdentifier to lock balance consistently with pallet_staking.
+pub trait StakingHandler<T: frame_system::Trait + membership::Trait + pallet_balances::Trait> {
+    /// Locks the specified balance on the account using specific lock identifier.
+    fn lock(account_id: &T::AccountId, amount: BalanceOf<T>);
+
+    /// Removes the specified lock on the account.
+    fn unlock(account_id: &T::AccountId);
+
+    /// Slash the specified balance on the account using specific lock identifier.
+    /// No limits, no actions on zero stake.
+    /// If slashing balance greater than the existing stake - stake is slashed to zero.
+    /// Returns actually slashed balance.
+    fn slash(account_id: &T::AccountId, amount: Option<BalanceOf<T>>) -> BalanceOf<T>;
+
+    /// Sets the new stake to a given amount.
+    fn set_stake(account_id: &T::AccountId, new_stake: BalanceOf<T>) -> DispatchResult;
+
+    /// Verifies that staking account bound to the member.
+    fn is_member_staking_account(member_id: &MemberId<T>, account_id: &T::AccountId) -> bool;
+
+    /// Verifies that there no conflicting stakes on the staking account.
+    fn is_account_free_of_conflicting_stakes(account_id: &T::AccountId) -> bool;
+
+    /// Verifies that staking account balance is sufficient for staking.
+    /// During the balance check we should consider already locked stake. Effective balance to check
+    /// is 'already locked funds' + 'usable funds'.
+    fn is_enough_balance_for_stake(account_id: &T::AccountId, amount: BalanceOf<T>) -> bool;
+
+    /// Returns the current stake on the account.
+    fn current_stake(account_id: &T::AccountId) -> BalanceOf<T>;
+}
+
 /// Implementation of the StakingHandler.
 pub struct StakingManager<
     T: frame_system::Trait + membership::Trait + pallet_balances::Trait,
@@ -79,6 +131,7 @@ impl<
         Ok(())
     }
 
+    // Membership support for staking accounts required.
     fn is_member_staking_account(_member_id: &MemberId<T>, _account_id: &T::AccountId) -> bool {
         true
     }

+ 106 - 0
runtime-modules/staking-handler/src/mock.rs

@@ -0,0 +1,106 @@
+use frame_support::{impl_outer_origin, parameter_types};
+use frame_system;
+use sp_core::H256;
+use sp_runtime::{
+    testing::Header,
+    traits::{BlakeTwo256, IdentityLookup},
+    Perbill,
+};
+
+impl_outer_origin! {
+    pub enum Origin for Test {}
+}
+
+mod membership_mod {
+    pub use membership::Event;
+}
+
+parameter_types! {
+    pub const BlockHashCount: u64 = 250;
+    pub const MaximumBlockWeight: u32 = 1024;
+    pub const MaximumBlockLength: u32 = 2 * 1024;
+    pub const AvailableBlockRatio: Perbill = Perbill::one();
+    pub const MinimumPeriod: u64 = 5;
+    pub const StakePoolId: [u8; 8] = *b"joystake";
+    pub const ExistentialDeposit: u32 = 0;
+}
+
+// Workaround for https://github.com/rust-lang/rust/issues/26925 - remove when sorted.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct Test;
+
+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 = pallet_balances::AccountData<u64>;
+    type OnNewAccount = ();
+    type OnKilledAccount = ();
+    type SystemWeightInfo = ();
+}
+
+impl pallet_balances::Trait for Test {
+    type Balance = u64;
+    type DustRemoval = ();
+    type Event = ();
+    type ExistentialDeposit = ExistentialDeposit;
+    type AccountStore = System;
+    type WeightInfo = ();
+    type MaxLocks = ();
+}
+
+impl membership::Trait for Test {
+    type Event = ();
+    type MemberId = u64;
+    type PaidTermId = u64;
+    type SubscriptionId = u64;
+    type ActorId = u64;
+}
+
+impl common::currency::GovernanceCurrency for Test {
+    type Currency = Balances;
+}
+
+impl pallet_timestamp::Trait for Test {
+    type Moment = u64;
+    type OnTimestampSet = ();
+    type MinimumPeriod = MinimumPeriod;
+    type WeightInfo = ();
+}
+
+pub type Balances = pallet_balances::Module<Test>;
+pub type System = frame_system::Module<Test>;
+pub type TestStakingManager = crate::StakingManager<Test, LockId>;
+
+parameter_types! {
+    pub const RewardPeriod: u32 = 2;
+    pub const MaxWorkerNumberLimit: u32 = 3;
+    pub const MinUnstakingPeriodLimit: u64 = 3;
+    pub const LockId: [u8; 8] = [1; 8];
+}
+
+pub fn build_test_externalities() -> sp_io::TestExternalities {
+    let t = frame_system::GenesisConfig::default()
+        .build_storage::<Test>()
+        .unwrap();
+
+    t.into()
+}

+ 222 - 0
runtime-modules/staking-handler/src/test.rs

@@ -0,0 +1,222 @@
+use super::mock::*;
+use crate::*;
+
+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,
+        );
+    }
+    assert_eq!(Balances::total_issuance(), initial_balance + balance);
+}
+
+#[test]
+fn lock_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 100;
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount);
+
+        TestStakingManager::lock(&account_id, stake);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount - stake);
+    });
+}
+
+#[test]
+fn unlock_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 100;
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+
+        TestStakingManager::lock(&account_id, stake);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount - stake);
+
+        TestStakingManager::unlock(&account_id);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount);
+    });
+}
+
+#[test]
+fn slash_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 100;
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+
+        TestStakingManager::lock(&account_id, stake);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount - stake);
+
+        let slash_amount = 50;
+        TestStakingManager::slash(&account_id, Some(slash_amount));
+        TestStakingManager::unlock(&account_id);
+
+        assert_eq!(
+            Balances::usable_balance(&account_id),
+            total_amount - slash_amount
+        );
+    });
+}
+
+#[test]
+fn slash_full_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 100;
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+
+        TestStakingManager::lock(&account_id, stake);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount - stake);
+
+        TestStakingManager::slash(&account_id, None);
+        TestStakingManager::unlock(&account_id);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount - stake);
+
+        // Lock was removed.
+        assert!(TestStakingManager::is_account_free_of_conflicting_stakes(
+            &account_id
+        ));
+    });
+}
+
+#[test]
+fn slash_down_to_zero_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 100;
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+
+        TestStakingManager::lock(&account_id, stake);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount - stake);
+
+        TestStakingManager::slash(&account_id, Some(stake));
+        TestStakingManager::unlock(&account_id);
+
+        assert_eq!(Balances::usable_balance(&account_id), total_amount - stake);
+
+        // Lock was removed.
+        assert!(TestStakingManager::is_account_free_of_conflicting_stakes(
+            &account_id
+        ));
+    });
+}
+
+#[test]
+fn current_stake_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 100;
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+
+        TestStakingManager::lock(&account_id, stake);
+
+        assert_eq!(TestStakingManager::current_stake(&account_id), stake);
+    });
+}
+
+#[test]
+fn is_account_free_of_conflicting_stakes_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 100;
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+        assert!(TestStakingManager::is_account_free_of_conflicting_stakes(
+            &account_id
+        ));
+
+        TestStakingManager::lock(&account_id, stake);
+
+        assert!(!TestStakingManager::is_account_free_of_conflicting_stakes(
+            &account_id
+        ));
+    });
+}
+
+#[test]
+fn is_enough_balance_for_stake_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 100;
+
+        assert!(!TestStakingManager::is_enough_balance_for_stake(
+            &account_id,
+            stake
+        ));
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+
+        assert!(TestStakingManager::is_enough_balance_for_stake(
+            &account_id,
+            stake
+        ));
+    });
+}
+
+// Test stub: not implemented yet.
+#[ignore]
+#[test]
+fn is_member_staking_account_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let member_id = 1;
+
+        assert!(TestStakingManager::is_member_staking_account(
+            &member_id,
+            &account_id
+        ));
+    });
+}
+
+#[test]
+fn set_stake_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let account_id = 1;
+        let total_amount = 300;
+        let stake = 300;
+        let invalid_stake = 600;
+
+        increase_total_balance_issuance_using_account_id(account_id, total_amount);
+
+        assert_eq!(
+            TestStakingManager::set_stake(&account_id, stake),
+            DispatchResult::Ok(())
+        );
+
+        assert_eq!(TestStakingManager::current_stake(&account_id), stake);
+
+        assert_eq!(
+            TestStakingManager::set_stake(&account_id, invalid_stake),
+            DispatchResult::Err(DispatchError::Other("Not enough balance for a new stake."))
+        );
+
+        assert_eq!(TestStakingManager::current_stake(&account_id), stake);
+    });
+}

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

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-storage'
-version = '3.1.0'
+version = '4.0.0'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -25,6 +25,7 @@ stake = { package = 'pallet-stake', default-features = false, path = '../stake'}
 hiring = { package = 'pallet-hiring', default-features = false, path = '../hiring'}
 minting = { package = 'pallet-token-mint', default-features = false, path = '../token-minting'}
 recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../recurring-reward'}
+staking-handler = { package = 'staking-handler', default-features = false, path = '../staking-handler'}
 
 [features]
 default = ['std']

+ 6 - 4
runtime-modules/storage/src/data_directory.rs

@@ -36,9 +36,11 @@ use serde::{Deserialize, Serialize};
 use common::origin::ActorOriginValidator;
 pub(crate) use common::BlockAndTime;
 
+use working_group::ensure_worker_signed;
+
 use crate::data_object_type_registry;
 use crate::data_object_type_registry::IsActiveDataObjectType;
-use crate::{MemberId, StorageProviderId, StorageWorkingGroup, StorageWorkingGroupInstance};
+use crate::{MemberId, StorageProviderId, StorageWorkingGroupInstance};
 
 /// The _Data directory_ main _Trait_.
 pub trait Trait:
@@ -208,7 +210,7 @@ decl_module! {
             size: u64,
             ipfs_content_id: Vec<u8>
         ) {
-            T::MemberOriginValidator::ensure_actor_origin(
+            <T as Trait>::MemberOriginValidator::ensure_actor_origin(
                 origin,
                 member_id,
             )?;
@@ -248,7 +250,7 @@ decl_module! {
             storage_provider_id: StorageProviderId<T>,
             content_id: T::ContentId
         ) {
-            <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
+            ensure_worker_signed::<T, StorageWorkingGroupInstance>(origin, &storage_provider_id)?;
 
             // == MUTATION SAFE ==
 
@@ -267,7 +269,7 @@ decl_module! {
             storage_provider_id: StorageProviderId<T>,
             content_id: T::ContentId
         ) {
-            <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
+            ensure_worker_signed::<T, StorageWorkingGroupInstance>(origin, &storage_provider_id)?;
 
             // == MUTATION SAFE ==
 

+ 5 - 3
runtime-modules/storage/src/data_object_storage_registry.rs

@@ -30,7 +30,9 @@ use sp_runtime::traits::{MaybeSerialize, Member};
 use sp_std::vec::Vec;
 
 use crate::data_directory::{self, ContentIdExists};
-use crate::{StorageProviderId, StorageWorkingGroup, StorageWorkingGroupInstance};
+use crate::{StorageProviderId, StorageWorkingGroupInstance};
+
+use working_group::ensure_worker_signed;
 
 const DEFAULT_FIRST_RELATIONSHIP_ID: u8 = 1;
 
@@ -144,7 +146,7 @@ decl_module! {
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn add_relationship(origin, storage_provider_id: StorageProviderId<T>, cid: T::ContentId) {
             // Origin should match storage provider.
-            <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
+            ensure_worker_signed::<T, StorageWorkingGroupInstance>(origin, &storage_provider_id)?;
 
             // Content ID must exist
             ensure!(T::ContentIdExists::has_content(&cid), Error::<T>::CidNotFound);
@@ -209,7 +211,7 @@ impl<T: Trait> Module<T> {
         id: T::DataObjectStorageRelationshipId,
         ready: bool,
     ) -> DispatchResult {
-        <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
+        ensure_worker_signed::<T, StorageWorkingGroupInstance>(origin, &storage_provider_id)?;
 
         // For that, we need to fetch the identified DOSR
         let mut dosr =

+ 7 - 5
runtime-modules/storage/src/data_object_type_registry.rs

@@ -30,7 +30,9 @@ use sp_arithmetic::traits::BaseArithmetic;
 use sp_runtime::traits::{MaybeSerialize, Member};
 use sp_std::vec::Vec;
 
-use crate::{StorageWorkingGroup, StorageWorkingGroupInstance};
+use crate::StorageWorkingGroupInstance;
+
+use working_group::ensure_origin_is_active_leader;
 
 const DEFAULT_TYPE_DESCRIPTION: &str = "Default data object type for audio and video content.";
 const DEFAULT_FIRST_DATA_OBJECT_TYPE_ID: u8 = 1;
@@ -138,7 +140,7 @@ decl_module! {
         /// Registers the new data object type. Requires leader privileges.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn register_data_object_type(origin, data_object_type: DataObjectType) {
-            <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
+            ensure_origin_is_active_leader::<T, StorageWorkingGroupInstance>(origin)?;
 
             let new_do_type_id = Self::next_data_object_type_id();
             let do_type: DataObjectType = DataObjectType {
@@ -159,7 +161,7 @@ decl_module! {
         /// Updates existing data object type. Requires leader privileges.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn update_data_object_type(origin, id: T::DataObjectTypeId, data_object_type: DataObjectType) {
-            <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
+            ensure_origin_is_active_leader::<T, StorageWorkingGroupInstance>(origin)?;
 
             let mut do_type = Self::ensure_data_object_type(id)?;
 
@@ -178,7 +180,7 @@ decl_module! {
         /// Activates existing data object type. Requires leader privileges.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn activate_data_object_type(origin, id: T::DataObjectTypeId) {
-            <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
+            ensure_origin_is_active_leader::<T, StorageWorkingGroupInstance>(origin)?;
 
             let mut do_type = Self::ensure_data_object_type(id)?;
 
@@ -196,7 +198,7 @@ decl_module! {
         /// Deactivates existing data object type. Requires leader privileges.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn deactivate_data_object_type(origin, id: T::DataObjectTypeId) {
-            <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
+            ensure_origin_is_active_leader::<T, StorageWorkingGroupInstance>(origin)?;
 
             let mut do_type = Self::ensure_data_object_type(id)?;
 

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

@@ -10,9 +10,6 @@ mod tests;
 // The storage working group instance alias.
 pub type StorageWorkingGroupInstance = working_group::Instance2;
 
-// Alias for storage working group
-pub(crate) type StorageWorkingGroup<T> = working_group::Module<T, StorageWorkingGroupInstance>;
-
 // Alias for the member id.
 pub(crate) type MemberId<T> = <T as membership::Trait>::MemberId;
 

+ 8 - 3
runtime-modules/storage/src/tests/data_object_type_registry.rs

@@ -12,11 +12,16 @@ const DEFAULT_LEADER_WORKER_ID: u32 = 1;
 struct SetLeadFixture;
 impl SetLeadFixture {
     fn set_default_lead() {
-        let worker = working_group::Worker {
+        let worker = working_group::Worker::<Test> {
             member_id: DEFAULT_LEADER_MEMBER_ID,
             role_account_id: DEFAULT_LEADER_ACCOUNT_ID,
-            reward_relationship: None,
-            role_stake_profile: None,
+            staking_account_id: None,
+            reward_account_id: DEFAULT_LEADER_ACCOUNT_ID,
+            started_leaving_at: None,
+            job_unstaking_period: 0,
+            reward_per_block: None,
+            missed_reward: None,
+            created_at: 1,
         };
 
         // Create the worker.

+ 22 - 12
runtime-modules/storage/src/tests/mock.rs

@@ -1,7 +1,7 @@
 #![cfg(test)]
 
 use frame_support::storage::StorageMap;
-use frame_support::traits::{OnFinalize, OnInitialize};
+use frame_support::traits::{LockIdentifier, OnFinalize, OnInitialize};
 use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
 use sp_core::H256;
 use sp_runtime::{
@@ -152,11 +152,24 @@ impl GovernanceCurrency for Test {
 
 parameter_types! {
     pub const MaxWorkerNumberLimit: u32 = 3;
+    pub const LockId: LockIdentifier = [2; 8];
 }
 
 impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = MetaEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, LockId>;
+    type MemberOriginValidator = ();
+    type MinUnstakingPeriodLimit = ();
+    type RewardPeriod = ();
+}
+
+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 data_object_type_registry::Trait for Test {
@@ -179,14 +192,6 @@ impl crate::data_directory::StorageProviderHelper<Test> for () {
     }
 }
 
-impl common::origin::ActorOriginValidator<Origin, u64, u64> for () {
-    fn ensure_actor_origin(origin: Origin, _account_id: u64) -> Result<u64, &'static str> {
-        let signed_account_id = frame_system::ensure_signed(origin)?;
-
-        Ok(signed_account_id)
-    }
-}
-
 impl data_object_storage_registry::Trait for Test {
     type Event = MetaEvent;
     type DataObjectStorageRelationshipId = u64;
@@ -320,11 +325,16 @@ pub(crate) fn hire_storage_provider() -> (u64, u32) {
     let storage_provider_id = 1;
     let role_account_id = 1;
 
-    let storage_provider = working_group::Worker {
+    let storage_provider = working_group::Worker::<Test> {
         member_id: 1,
         role_account_id,
-        reward_relationship: None,
-        role_stake_profile: None,
+        staking_account_id: None,
+        reward_account_id: role_account_id,
+        started_leaving_at: None,
+        job_unstaking_period: 0,
+        reward_per_block: None,
+        missed_reward: None,
+        created_at: 1,
     };
 
     <working_group::WorkerById<Test, StorageWorkingGroupInstance>>::insert(

+ 16 - 19
runtime-modules/working-group/Cargo.toml

@@ -1,44 +1,41 @@
 [package]
-name = 'pallet-working-group'
-version = '3.1.0'
+name = "pallet-working-group"
+version = "4.0.0"
 authors = ['Joystream contributors']
 edition = '2018'
 
 [dependencies]
 serde = { version = "1.0.101", optional = true, features = ["derive"] }
-codec = { package = 'parity-scale-codec', version = '1.3.4', default-features = false, features = ['derive'] }
-sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+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'}
 frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
-stake = { package = 'pallet-stake', default-features = false, path = '../stake'}
-hiring = { package = 'pallet-hiring', default-features = false, path = '../hiring'}
-minting = { package = 'pallet-token-mint', default-features = false, path = '../token-minting'}
-recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../recurring-reward'}
+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 = 'staking-handler', default-features = false, path = '../staking-handler'}
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 
 [features]
 default = ['std']
+runtime-benchmarks = ["frame-benchmarking"]
 std = [
 	'serde',
 	'codec/std',
-	'sp-std/std',
+	'sp-runtime/std',
 	'frame-support/std',
 	'frame-system/std',
 	'sp-arithmetic/std',
-	'sp-runtime/std',
-	'membership/std',
-	'stake/std',
-	'hiring/std',
-	'minting/std',
-	'recurringrewards/std',
+	'sp-std/std',
 	'common/std',
-]
+	'membership/std',
+	'balances/std',
+	'staking-handler/std',
+]

+ 1126 - 0
runtime-modules/working-group/src/benchmarking.rs

@@ -0,0 +1,1126 @@
+#![cfg(feature = "runtime-benchmarks")]
+use super::*;
+use core::convert::TryInto;
+use frame_benchmarking::{account, benchmarks_instance, Zero};
+use frame_support::traits::OnInitialize;
+use frame_system::EventRecord;
+use frame_system::Module as System;
+use frame_system::RawOrigin;
+use sp_runtime::traits::Bounded;
+use sp_std::cmp::min;
+use sp_std::prelude::*;
+use system as frame_system;
+
+use crate::types::StakeParameters;
+use crate::Module as WorkingGroup;
+use membership::Module as Membership;
+
+const SEED: u32 = 0;
+const MAX_BYTES: u32 = 16384;
+
+enum StakingRole {
+    WithStakes,
+    WithoutStakes,
+}
+
+fn assert_last_event<T: Trait<I>, I: Instance>(generic_event: <T as Trait<I>>::Event) {
+    let events = System::<T>::events();
+    let system_event: <T as frame_system::Trait>::Event = generic_event.into();
+    // compare to the last event record
+    let EventRecord { event, .. } = &events[events.len() - 1];
+    assert_eq!(event, &system_event);
+}
+
+fn get_byte(num: u32, byte_number: u8) -> u8 {
+    ((num & (0xff << (8 * byte_number))) >> 8 * byte_number) as u8
+}
+
+fn add_opening_helper<T: Trait<I>, I: Instance>(
+    id: u32,
+    add_opening_origin: &T::Origin,
+    staking_role: &StakingRole,
+    job_opening_type: &OpeningType,
+) -> T::OpeningId {
+    let staking_policy = match staking_role {
+        StakingRole::WithStakes => Some(StakePolicy {
+            stake_amount: One::one(),
+            leaving_unstaking_period: T::MinUnstakingPeriodLimit::get() + One::one(),
+        }),
+        StakingRole::WithoutStakes => None,
+    };
+
+    WorkingGroup::<T, _>::add_opening(
+        add_opening_origin.clone(),
+        vec![],
+        *job_opening_type,
+        staking_policy,
+        Some(RewardPolicy {
+            reward_per_block: One::one(),
+        }),
+    )
+    .unwrap();
+
+    let opening_id = T::OpeningId::from(id.try_into().unwrap());
+
+    assert!(
+        OpeningById::<T, I>::contains_key(opening_id),
+        "Opening not added"
+    );
+
+    opening_id
+}
+
+fn apply_on_opening_helper<T: Trait<I>, I: Instance>(
+    id: u32,
+    staking_role: &StakingRole,
+    applicant_id: &T::AccountId,
+    member_id: &T::MemberId,
+    opening_id: &T::OpeningId,
+) -> T::ApplicationId {
+    let stake_parameters = match staking_role {
+        StakingRole::WithStakes => Some(StakeParameters {
+            // Due to mock implementation of StakingHandler we can't go over 1000
+            stake: min(
+                BalanceOfCurrency::<T>::max_value(),
+                BalanceOfCurrency::<T>::from(1000),
+            ),
+            staking_account_id: applicant_id.clone(),
+        }),
+        StakingRole::WithoutStakes => None,
+    };
+
+    WorkingGroup::<T, _>::apply_on_opening(
+        RawOrigin::Signed(applicant_id.clone()).into(),
+        ApplyOnOpeningParameters::<T, I> {
+            member_id: *member_id,
+            opening_id: *opening_id,
+            role_account_id: applicant_id.clone(),
+            reward_account_id: applicant_id.clone(),
+            description: vec![],
+            stake_parameters,
+        },
+    )
+    .unwrap();
+
+    let application_id = T::ApplicationId::from(id.try_into().unwrap());
+
+    assert!(
+        ApplicationById::<T, I>::contains_key(application_id),
+        "Application not added"
+    );
+
+    application_id
+}
+
+fn add_opening_and_apply_with_multiple_ids<T: Trait<I>, I: Instance>(
+    ids: &Vec<u32>,
+    add_opening_origin: &T::Origin,
+    staking_role: &StakingRole,
+    job_opening_type: &OpeningType,
+) -> (T::OpeningId, BTreeSet<T::ApplicationId>, Vec<T::AccountId>) {
+    let opening_id =
+        add_opening_helper::<T, I>(1, add_opening_origin, &staking_role, job_opening_type);
+
+    let mut successful_application_ids = BTreeSet::new();
+
+    let mut account_ids = Vec::new();
+    for id in ids.iter() {
+        let (applicant_account_id, applicant_member_id) = member_funded_account::<T>("member", *id);
+        let application_id = apply_on_opening_helper::<T, I>(
+            *id,
+            &staking_role,
+            &applicant_account_id,
+            &applicant_member_id,
+            &opening_id,
+        );
+
+        successful_application_ids.insert(application_id);
+        account_ids.push(applicant_account_id);
+    }
+
+    (opening_id, successful_application_ids, account_ids)
+}
+
+fn add_and_apply_opening<T: Trait<I>, I: Instance>(
+    id: u32,
+    add_opening_origin: &T::Origin,
+    staking_role: &StakingRole,
+    applicant_id: &T::AccountId,
+    member_id: &T::MemberId,
+    job_opening_type: &OpeningType,
+) -> (T::OpeningId, T::ApplicationId) {
+    let opening_id =
+        add_opening_helper::<T, I>(id, add_opening_origin, staking_role, job_opening_type);
+
+    let application_id =
+        apply_on_opening_helper::<T, I>(id, staking_role, applicant_id, member_id, &opening_id);
+
+    (opening_id, application_id)
+}
+
+// Method to generate a distintic valid handle
+// for a membership. For each index.
+fn handle_from_id<T: membership::Trait>(id: u32) -> Vec<u8> {
+    let min_handle_length = Membership::<T>::min_handle_length();
+
+    let mut handle = vec![];
+
+    for i in 0..min(Membership::<T>::max_handle_length().try_into().unwrap(), 4) {
+        handle.push(get_byte(id, i));
+    }
+
+    while handle.len() < (min_handle_length as usize) {
+        handle.push(0u8);
+    }
+
+    handle
+}
+
+fn member_funded_account<T: 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);
+
+    let _ = <T as common::currency::GovernanceCurrency>::Currency::make_free_balance_be(
+        &account_id,
+        BalanceOfCurrency::<T>::max_value(),
+    );
+
+    let authority_account = account::<T::AccountId>(name, 0, SEED);
+
+    Membership::<T>::set_screening_authority(RawOrigin::Root.into(), authority_account.clone())
+        .unwrap();
+
+    Membership::<T>::add_screened_member(
+        RawOrigin::Signed(authority_account.clone()).into(),
+        account_id.clone(),
+        Some(handle),
+        None,
+        None,
+    )
+    .unwrap();
+
+    (account_id, T::MemberId::from(id.try_into().unwrap()))
+}
+
+fn force_missed_reward<T: Trait<I>, I: Instance>() {
+    let curr_block_number =
+        System::<T>::block_number().saturating_add(T::RewardPeriod::get().into());
+    System::<T>::set_block_number(curr_block_number);
+    WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), Zero::zero()).unwrap();
+    WorkingGroup::<T, _>::on_initialize(curr_block_number);
+}
+
+fn insert_a_worker<T: Trait<I>, I: Instance>(
+    staking_role: StakingRole,
+    job_opening_type: OpeningType,
+    id: u32,
+    lead_id: Option<T::AccountId>,
+) -> (T::AccountId, WorkerId<T>)
+where
+    WorkingGroup<T, I>: OnInitialize<T::BlockNumber>,
+{
+    let add_worker_origin = match job_opening_type {
+        OpeningType::Leader => RawOrigin::Root,
+        OpeningType::Regular => RawOrigin::Signed(lead_id.clone().unwrap()),
+    };
+
+    let (caller_id, member_id) = member_funded_account::<T>("member", id);
+
+    let (opening_id, application_id) = add_and_apply_opening::<T, I>(
+        id,
+        &T::Origin::from(add_worker_origin.clone()),
+        &staking_role,
+        &caller_id,
+        &member_id,
+        &job_opening_type,
+    );
+
+    let mut successful_application_ids = BTreeSet::<T::ApplicationId>::new();
+    successful_application_ids.insert(application_id);
+    WorkingGroup::<T, _>::fill_opening(
+        add_worker_origin.clone().into(),
+        opening_id,
+        successful_application_ids,
+    )
+    .unwrap();
+
+    // Every worst case either include or doesn't mind having a non-zero
+    // remaining reward
+    force_missed_reward::<T, I>();
+
+    let worker_id = WorkerId::<T>::from(id.try_into().unwrap());
+
+    assert!(WorkerById::<T, I>::contains_key(worker_id));
+
+    (caller_id, worker_id)
+}
+
+benchmarks_instance! {
+    _ { }
+
+    on_initialize_leaving {
+        let i in 1 .. T::MaxWorkerNumberLimit::get();
+
+        let (lead_id, lead_worker_id) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Leader,
+            0,
+            None
+        );
+
+        let (opening_id, successful_application_ids, application_account_id) =
+            add_opening_and_apply_with_multiple_ids::<T, I>(
+                &(1..i).collect(),
+                &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
+                &StakingRole::WithStakes,
+                &OpeningType::Regular
+            );
+
+        WorkingGroup::<T, _>::fill_opening(
+            RawOrigin::Signed(lead_id.clone()).into(),
+            opening_id,
+            successful_application_ids.clone()
+        ).unwrap();
+
+        force_missed_reward::<T,I>();
+
+        // Force all workers to leave (Including the lead)
+        // We should have every WorkerId from 0 to i-1
+        // Corresponding to each account id
+        let mut worker_id = Zero::zero();
+        for id in application_account_id {
+            worker_id += One::one();
+            WorkingGroup::<T, _>::leave_role(RawOrigin::Signed(id).into(), worker_id).unwrap();
+        }
+
+        // Worst case scenario one of the leaving workers is the lead
+        WorkingGroup::<T, _>::leave_role(
+            RawOrigin::Signed(lead_id).into(),
+            lead_worker_id
+        ).unwrap();
+
+        for i in 1..successful_application_ids.len() {
+            let worker = WorkerId::<T>::from(i.try_into().unwrap());
+            assert!(WorkerById::<T, I>::contains_key(worker), "Not all workers added");
+            assert_eq!(
+                WorkingGroup::<T, _>::worker_by_id(worker).started_leaving_at,
+                Some(System::<T>::block_number()),
+                "Worker hasn't started leaving"
+            );
+        }
+
+        // Maintain consistency with add_opening_helper
+        let leaving_unstaking_period = T::MinUnstakingPeriodLimit::get() + One::one();
+
+        // Force unstaking period to have passed
+        let curr_block_number =
+            System::<T>::block_number().saturating_add(leaving_unstaking_period.into());
+        System::<T>::set_block_number(curr_block_number);
+        WorkingGroup::<T, _>::set_budget(
+            RawOrigin::Root.into(),
+            BalanceOfCurrency::<T>::max_value()
+        ).unwrap();
+
+        assert_eq!(WorkingGroup::<T, _>::budget(), BalanceOfCurrency::<T>::max_value());
+    }: { WorkingGroup::<T, _>::on_initialize(curr_block_number) }
+    verify {
+        WorkerById::<T, I>::iter().for_each(|(worker_id, _)| {
+            assert!(!WorkerById::<T, I>::contains_key(worker_id), "Worker hasn't left");
+        });
+
+        let reward_per_worker = BalanceOfCurrency::<T>::from(T::RewardPeriod::get());
+
+        assert_eq!(
+            WorkingGroup::<T, I>::budget(),
+            BalanceOfCurrency::<T>::max_value()
+                .saturating_sub(BalanceOfCurrency::<T>::from(i) * reward_per_worker)
+                .saturating_sub(reward_per_worker),
+            "Budget wasn't correctly updated, probably not all workers rewarded"
+        );
+    }
+
+
+    on_initialize_rewarding_with_missing_reward {
+        let i in 1 .. T::MaxWorkerNumberLimit::get();
+
+        let (lead_id, _) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Leader,
+            0,
+            None
+        );
+
+        let (opening_id, successful_application_ids, _) =
+            add_opening_and_apply_with_multiple_ids::<T, I>(
+                &(1..i).collect(),
+                &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
+                &StakingRole::WithStakes,
+                &OpeningType::Regular
+            );
+
+        WorkingGroup::<T, _>::fill_opening(RawOrigin::Signed(lead_id.clone()).into(), opening_id,
+        successful_application_ids.clone()).unwrap();
+
+        for i in 1..successful_application_ids.len() {
+            assert!(
+                WorkerById::<T, I>::contains_key(WorkerId::<T>::from(i.try_into().unwrap())),
+                "Not all workers added"
+            );
+        }
+
+        // Worst case scenario there is a missing reward
+        force_missed_reward::<T, I>();
+
+        // Sets periods so that we can reward
+        let curr_block_number =
+            System::<T>::block_number().saturating_add(T::RewardPeriod::get().into());
+        System::<T>::set_block_number(curr_block_number);
+
+        // Sets budget so that we can pay it
+        WorkingGroup::<T, _>::set_budget(
+            RawOrigin::Root.into(),
+            BalanceOfCurrency::<T>::max_value()
+        ).unwrap();
+
+        assert_eq!(WorkingGroup::<T, _>::budget(), BalanceOfCurrency::<T>::max_value());
+    }: { WorkingGroup::<T, _>::on_initialize(curr_block_number) }
+    verify {
+        let reward_per_worker = BalanceOfCurrency::<T>::from(T::RewardPeriod::get());
+
+        let reward_for_workers =
+            BalanceOfCurrency::<T>::from(i) * reward_per_worker * BalanceOfCurrency::<T>::from(2);
+
+        assert_eq!(
+            WorkingGroup::<T, _>::budget(),
+            // When creating a worker using `insert_a_worker` it gives the lead a number of block
+            // equating to reward period as missed reward(and the reward value is 1) therefore the
+            // additional discount of balance
+            BalanceOfCurrency::<T>::max_value()
+                .saturating_sub(reward_for_workers)
+                .saturating_sub(reward_per_worker),
+            "Budget wasn't correctly updated, probably not all workers rewarded"
+        );
+    }
+
+    on_initialize_rewarding_with_missing_reward_cant_pay {
+        let i in 1 .. T::MaxWorkerNumberLimit::get();
+
+        let (lead_id, _) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Leader,
+            0,
+            None
+        );
+
+        let (opening_id, successful_application_ids, _) =
+            add_opening_and_apply_with_multiple_ids::<T, I>(
+                &(1..i).collect(),
+                &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
+                &StakingRole::WithStakes,
+                &OpeningType::Regular
+            );
+
+        WorkingGroup::<T, _>::fill_opening(RawOrigin::Signed(lead_id.clone()).into(), opening_id,
+        successful_application_ids.clone()).unwrap();
+
+        for i in 1..successful_application_ids.len() {
+            assert!(
+                WorkerById::<T, I>::contains_key(WorkerId::<T>::from(i.try_into().unwrap())),
+                "Not all workers added"
+            );
+        }
+
+        // Sets periods so that we can reward
+        let curr_block_number =
+            System::<T>::block_number().saturating_add(T::RewardPeriod::get().into());
+
+        System::<T>::set_block_number(curr_block_number);
+
+        // Sets budget so that we can't pay it
+        WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), Zero::zero()).unwrap();
+
+        assert_eq!(WorkingGroup::<T, _>::budget(), Zero::zero());
+
+    }: { WorkingGroup::<T, _>::on_initialize(curr_block_number) }
+    verify {
+        WorkerById::<T, I>::iter().for_each(|(_, worker)| {
+            let missed_reward = worker.missed_reward.expect("There should be some missed reward");
+
+            assert!(
+                missed_reward >= BalanceOfCurrency::<T>::from(T::RewardPeriod::get()),
+                "At least one worker wasn't rewarded"
+            );
+        });
+    }
+
+    on_initialize_rewarding_without_missing_reward {
+        let i in 1 .. T::MaxWorkerNumberLimit::get();
+
+        let (lead_id, _) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Leader,
+            0,
+            None
+        );
+
+        let (opening_id, successful_application_ids, _) =
+            add_opening_and_apply_with_multiple_ids::<T, I>(
+                &(1..i).collect(),
+                &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
+                &StakingRole::WithStakes,
+                &OpeningType::Regular
+            );
+
+        WorkingGroup::<T, _>::fill_opening(RawOrigin::Signed(lead_id.clone()).into(), opening_id,
+        successful_application_ids.clone()).unwrap();
+
+        for i in 1..successful_application_ids.len() {
+            assert!(
+                WorkerById::<T, I>::contains_key(WorkerId::<T>::from(i.try_into().unwrap())),
+                "Not all workers added"
+            );
+        }
+
+        // Sets periods so that we can reward
+        let curr_block_number =
+            System::<T>::block_number().saturating_add(T::RewardPeriod::get().into());
+        System::<T>::set_block_number(curr_block_number);
+
+        // Sets budget so that we can pay it
+        WorkingGroup::<T, _>::set_budget(
+            RawOrigin::Root.into(), BalanceOfCurrency::<T>::max_value()
+        ).unwrap();
+        assert_eq!(WorkingGroup::<T, _>::budget(), BalanceOfCurrency::<T>::max_value());
+
+    }: { WorkingGroup::<T, _>::on_initialize(curr_block_number) }
+    verify {
+        let reward_per_worker = BalanceOfCurrency::<T>::from(T::RewardPeriod::get());
+        let workers_total_reward = BalanceOfCurrency::<T>::from(i) * reward_per_worker;
+        assert_eq!(
+            WorkingGroup::<T, _>::budget(),
+            // When creating a worker using `insert_a_worker` it gives the lead a number of block
+            // equating to reward period as missed reward(and the reward value is 1) therefore the
+            // additional discount of balance
+            BalanceOfCurrency::<T>::max_value()
+                .saturating_sub(workers_total_reward)
+                .saturating_sub(reward_per_worker),
+            "Budget wasn't correctly updated, probably not all workers rewarded"
+        );
+    }
+
+    apply_on_opening {
+        let i in 1 .. MAX_BYTES;
+
+        let (lead_account_id, lead_member_id) = member_funded_account::<T>("lead", 0);
+        let opening_id = add_opening_helper::<T, I>(
+            0,
+            &T::Origin::from(RawOrigin::Root),
+            &StakingRole::WithStakes,
+            &OpeningType::Leader
+        );
+
+        let apply_on_opening_params = ApplyOnOpeningParameters::<T, I> {
+            member_id: lead_member_id,
+            opening_id: opening_id.clone(),
+            role_account_id: lead_account_id.clone(),
+            reward_account_id: lead_account_id.clone(),
+            description: vec![0u8; i.try_into().unwrap()],
+            stake_parameters: Some(
+                // Make sure to keep consistency with the StakePolicy in add_opening_helper
+                // (we are safe as long as we are using max_value for stake)
+                StakeParameters {
+                    stake: One::one(),
+                    staking_account_id: lead_account_id.clone(),
+                }
+            ),
+        };
+
+    }: _ (RawOrigin::Signed(lead_account_id.clone()), apply_on_opening_params)
+    verify {
+        assert!(
+            ApplicationById::<T, I>::contains_key(T::ApplicationId::from(0)),
+            "Application not found"
+        );
+
+        assert_last_event::<T, I>(RawEvent::AppliedOnOpening(opening_id, Zero::zero()).into());
+    }
+
+    fill_opening_lead {
+        let i in 0 .. 1;
+
+        let (lead_account_id, lead_member_id) = member_funded_account::<T>("lead", 0);
+        let (opening_id, application_id) = add_and_apply_opening::<T, I>(
+            0,
+            &RawOrigin::Root.into(),
+            &StakingRole::WithoutStakes,
+            &lead_account_id,
+            &lead_member_id,
+            &OpeningType::Leader
+        );
+
+        let mut successful_application_ids: BTreeSet<T::ApplicationId> = BTreeSet::new();
+        successful_application_ids.insert(application_id);
+    }: fill_opening(RawOrigin::Root, opening_id, successful_application_ids)
+    verify {
+        assert!(!OpeningById::<T, I>::contains_key(opening_id), "Opening still not filled");
+
+        let worker_id = Zero::zero();
+
+        assert_eq!(
+            WorkingGroup::<T, I>::current_lead(),
+            Some(worker_id),
+            "Opening for lead not filled"
+        );
+
+        let mut application_id_to_worker_id = BTreeMap::new();
+        application_id_to_worker_id.insert(application_id, worker_id);
+
+        assert_last_event::<T, I>(
+            RawEvent::OpeningFilled(opening_id, application_id_to_worker_id).into()
+        );
+    }
+
+    fill_opening_worker {
+        let i in 1 .. T::MaxWorkerNumberLimit::get();
+        let (lead_id, lead_worker_id) = insert_a_worker::<T, I>(
+            StakingRole::WithoutStakes,
+            OpeningType::Leader,
+            0,
+            None
+        );
+
+        let (opening_id, successful_application_ids, _) =
+            add_opening_and_apply_with_multiple_ids::<T, I>(
+                &(1..i).collect(),
+                &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
+                &StakingRole::WithoutStakes,
+                &OpeningType::Regular
+            );
+    }: fill_opening(
+            RawOrigin::Signed(lead_id.clone()),
+            opening_id,
+            successful_application_ids.clone()
+        )
+    verify {
+        assert!(!OpeningById::<T, I>::contains_key(opening_id), "Opening still not filled");
+
+        let mut application_id_to_worker_id = BTreeMap::new();
+        for (i, application_id) in successful_application_ids.iter().enumerate() {
+            let worker_id = WorkerId::<T>::from((i + 1).try_into().unwrap());
+            application_id_to_worker_id.insert(*application_id, worker_id);
+            assert!(
+                WorkerById::<T, I>::contains_key(WorkerId::<T>::from(i.try_into().unwrap())),
+                "Not all workers added"
+            );
+        }
+
+        assert_last_event::<T, I>(
+            RawEvent::OpeningFilled(opening_id, application_id_to_worker_id).into()
+        );
+    }
+
+    update_role_account{
+        let i in 0 .. 1;
+        let (lead_id, lead_worker_id) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let new_account_id = account::<T::AccountId>("new_lead_account", 1, SEED);
+    }: _ (RawOrigin::Signed(lead_id), lead_worker_id, new_account_id.clone())
+    verify {
+        assert_eq!(
+            WorkingGroup::<T, I>::worker_by_id(lead_worker_id).role_account_id,
+            new_account_id,
+            "Role account notupdated"
+        );
+
+        assert_last_event::<T, I>(
+            RawEvent::WorkerRoleAccountUpdated(lead_worker_id, new_account_id).into()
+        );
+    }
+
+    cancel_opening {
+        let i in 0 .. 1;
+
+        let (lead_id, _) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let opening_id = add_opening_helper::<T, I>(
+            1,
+            &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
+            &StakingRole::WithoutStakes,
+            &OpeningType::Regular
+        );
+
+    }: _ (RawOrigin::Signed(lead_id.clone()), opening_id)
+    verify {
+        assert!(!OpeningById::<T, I>::contains_key(opening_id), "Opening not removed");
+        assert_last_event::<T, I>(RawEvent::OpeningCanceled(opening_id).into());
+    }
+
+    withdraw_application {
+        let i in 0 .. 1;
+
+        let (caller_id, member_id) = member_funded_account::<T>("lead", 0);
+        let (_, application_id) = add_and_apply_opening::<T, I>(0,
+            &RawOrigin::Root.into(),
+            &StakingRole::WithStakes,
+            &caller_id,
+            &member_id,
+            &OpeningType::Leader
+        );
+
+    }: _ (RawOrigin::Signed(caller_id.clone()), application_id)
+    verify {
+        assert!(!ApplicationById::<T, I>::contains_key(application_id), "Application not removed");
+        assert_last_event::<T, I>(RawEvent::ApplicationWithdrawn(application_id).into());
+    }
+
+    // Regular worker is the worst case scenario since the checks
+    // require access to the storage whilist that's not the case with a lead opening
+    slash_stake {
+        let i in 0 .. MAX_BYTES;
+
+        let (lead_id, lead_worker_id) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let (caller_id, worker_id) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Regular,
+            1,
+            Some(lead_id.clone())
+        );
+        let slashing_amount = One::one();
+        let penalty = Penalty {
+            slashing_text: vec![0u8; i.try_into().unwrap()],
+            slashing_amount,
+        };
+    }: _(RawOrigin::Signed(lead_id.clone()), worker_id, penalty)
+    verify {
+        assert_last_event::<T, I>(RawEvent::StakeSlashed(worker_id, slashing_amount).into());
+    }
+
+    terminate_role_worker {
+        let i in 0 .. MAX_BYTES;
+
+        let (lead_id, _) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let (caller_id, worker_id) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Regular,
+            1,
+            Some(lead_id.clone())
+        );
+        // To be able to pay unpaid reward
+        let current_budget = BalanceOfCurrency::<T>::max_value();
+        WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), current_budget).unwrap();
+        let penalty = Penalty {
+            slashing_text: vec![0u8; i.try_into().unwrap()],
+            slashing_amount: One::one(),
+        };
+    }: terminate_role(RawOrigin::Signed(lead_id.clone()), worker_id, Some(penalty))
+    verify {
+        assert!(!WorkerById::<T, I>::contains_key(worker_id), "Worker not terminated");
+        assert_last_event::<T, I>(RawEvent::TerminatedWorker(worker_id).into());
+    }
+
+    terminate_role_lead {
+        let i in 0 .. MAX_BYTES;
+
+        let (_, lead_worker_id) =
+            insert_a_worker::<T, I>(StakingRole::WithStakes, OpeningType::Leader, 0, None);
+        let current_budget = BalanceOfCurrency::<T>::max_value();
+        // To be able to pay unpaid reward
+        WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), current_budget).unwrap();
+        let penalty = Penalty {
+            slashing_text: vec![0u8; i.try_into().unwrap()],
+            slashing_amount: One::one(),
+        };
+    }: terminate_role(RawOrigin::Root, lead_worker_id, Some(penalty))
+    verify {
+        assert!(!WorkerById::<T, I>::contains_key(lead_worker_id), "Worker not terminated");
+        assert_last_event::<T, I>(RawEvent::TerminatedLeader(lead_worker_id).into());
+    }
+
+    // Regular worker is the worst case scenario since the checks
+    // require access to the storage whilist that's not the case with a lead opening
+    increase_stake {
+        let i in 0 .. 1;
+
+        let (lead_id, _) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let (caller_id, worker_id) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Regular,
+            1,
+            Some(lead_id.clone())
+        );
+
+        let old_stake = One::one();
+        WorkingGroup::<T, _>::decrease_stake(
+            RawOrigin::Signed(lead_id.clone()).into(), worker_id.clone(), old_stake).unwrap();
+        let new_stake = old_stake + One::one();
+    }: _ (RawOrigin::Signed(caller_id.clone()), worker_id.clone(), new_stake)
+    verify {
+        assert_last_event::<T, I>(RawEvent::StakeIncreased(worker_id, new_stake).into());
+    }
+
+    // Regular worker is the worst case scenario since the checks
+    // require access to the storage whilist that's not the case with a lead opening
+    decrease_stake {
+        let i in 0 .. 1;
+
+        let (lead_id, _) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let (_, worker_id) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Regular,
+            1,
+            Some(lead_id.clone())
+        );
+
+        // I'm assuming that we will usually have MaxBalance > 1
+        let new_stake = One::one();
+    }: _ (RawOrigin::Signed(lead_id), worker_id, new_stake)
+    verify {
+        assert_last_event::<T, I>(RawEvent::StakeDecreased(worker_id, new_stake).into());
+    }
+
+    spend_from_budget {
+        let i in 0 .. 1;
+
+        let (lead_id, _) = insert_a_worker::<T, I>(
+            StakingRole::WithoutStakes,
+            OpeningType::Leader,
+            0,
+            None
+        );
+
+        let current_budget = BalanceOfCurrency::<T>::max_value();
+        WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), current_budget).unwrap();
+    }: _ (RawOrigin::Signed(lead_id.clone()), lead_id.clone(), current_budget, None)
+    verify {
+        assert_eq!(WorkingGroup::<T, I>::budget(), Zero::zero(), "Budget not updated");
+        assert_last_event::<T, I>(RawEvent::BudgetSpending(lead_id, current_budget).into());
+    }
+
+    // Regular worker is the worst case scenario since the checks
+    // require access to the storage whilist that's not the case with a lead opening
+    update_reward_amount {
+        let i in 0 .. 1;
+
+        let (lead_id, _) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let (_, worker_id) = insert_a_worker::<T, I>(
+            StakingRole::WithoutStakes,
+            OpeningType::Regular,
+            1,
+            Some(lead_id.clone())
+        );
+
+        let new_reward = Some(BalanceOfCurrency::<T>::max_value());
+    }: _ (RawOrigin::Signed(lead_id.clone()), worker_id, new_reward)
+    verify {
+        assert_eq!(
+            WorkingGroup::<T, I>::worker_by_id(worker_id).reward_per_block,
+            new_reward,
+            "Reward not updated"
+        );
+
+        assert_last_event::<T, I>(
+            RawEvent::WorkerRewardAmountUpdated(worker_id, new_reward).into()
+        );
+    }
+
+    set_status_text {
+        let i in 0 .. MAX_BYTES;
+
+        let (lead_id, _) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let status_text = Some(vec![0u8; i.try_into().unwrap()]);
+
+    }: _ (RawOrigin::Signed(lead_id), status_text.clone())
+    verify {
+        let status_text_hash = T::Hashing::hash(&status_text.unwrap()).as_ref().to_vec();
+
+        assert_eq!(
+            WorkingGroup::<T, I>::status_text_hash(),
+            status_text_hash,
+            "Status text not updated"
+        );
+
+        assert_last_event::<T, I>(RawEvent::StatusTextChanged(status_text_hash).into());
+    }
+
+    update_reward_account {
+        let i in 0 .. 1;
+
+        let (caller_id, worker_id) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+        let new_id = account::<T::AccountId>("new_id", 1, 0);
+
+    }: _ (RawOrigin::Signed(caller_id), worker_id, new_id.clone())
+    verify {
+        assert_eq!(
+            WorkingGroup::<T, I>::worker_by_id(worker_id).reward_account_id,
+            new_id,
+            "Reward account not updated"
+        );
+
+        assert_last_event::<T, I>(RawEvent::WorkerRewardAccountUpdated(worker_id, new_id).into());
+    }
+
+    set_budget {
+        let i in 0 .. 1;
+
+        let new_budget = BalanceOfCurrency::<T>::max_value();
+
+    }: _(RawOrigin::Root, new_budget)
+    verify {
+        assert_eq!(WorkingGroup::<T, I>::budget(), new_budget, "Budget isn't updated");
+        assert_last_event::<T, I>(RawEvent::BudgetSet(new_budget).into());
+    }
+
+    // Regular opening is the worst case scenario since the checks
+    // require access to the storage whilist that's not the case with a lead opening
+    add_opening {
+        let i in 0 .. MAX_BYTES;
+
+        let (lead_id, _) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+
+        let stake_policy = StakePolicy {
+            stake_amount: BalanceOfCurrency::<T>::max_value(),
+            leaving_unstaking_period: T::BlockNumber::max_value(),
+        };
+
+        let reward_policy = RewardPolicy {
+            reward_per_block: BalanceOfCurrency::<T>::max_value(),
+        };
+
+        let description = vec![0u8; i.try_into().unwrap()];
+
+    }: _(
+            RawOrigin::Signed(lead_id),
+            description,
+            OpeningType::Regular,
+            Some(stake_policy),
+            Some(reward_policy)
+        )
+    verify {
+        assert!(OpeningById::<T, I>::contains_key(T::OpeningId::from(1)));
+        assert_last_event::<T, I>(RawEvent::OpeningAdded(T::OpeningId::from(1)).into());
+    }
+
+    // This is always worse than leave_role_immediatly
+    leave_role_immediatly {
+        let i in 0 .. 1;
+        // Worst case scenario there is a lead(this requires **always** more steps)
+        // could separate into new branch to tighten weight
+        // Also, workers without stake can leave immediatly
+        let (caller_id, lead_worker_id) =
+            insert_a_worker::<T, I>(StakingRole::WithoutStakes, OpeningType::Leader, 0, None);
+
+        // To be able to pay unpaid reward
+        WorkingGroup::<T, _>::set_budget(
+            RawOrigin::Root.into(),
+            BalanceOfCurrency::<T>::max_value()
+        ).unwrap();
+
+    }: leave_role(RawOrigin::Signed(caller_id), lead_worker_id)
+    verify {
+        assert!(!WorkerById::<T, I>::contains_key(lead_worker_id), "Worker hasn't left");
+        assert_last_event::<T, I>(RawEvent::WorkerExited(lead_worker_id).into());
+    }
+
+    // Generally speaking this seems to be always the best case scenario
+    // but since it's so obviously a different branch I think it's a good idea
+    // to leave this branch and use tha max between these 2
+    leave_role_later {
+        let i in 0 .. 1;
+
+        // Workers with stake can't leave immediatly
+        let (caller_id, caller_worker_id) = insert_a_worker::<T, I>(
+            StakingRole::WithStakes,
+            OpeningType::Leader,
+            0,
+            None
+        );
+    }: leave_role(RawOrigin::Signed(caller_id), caller_worker_id)
+    verify {
+        assert_eq!(
+            WorkingGroup::<T, _>::worker_by_id(caller_worker_id).started_leaving_at,
+            Some(System::<T>::block_number()),
+            "Worker hasn't started leaving"
+        );
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::tests::{build_test_externalities, Test};
+    use frame_support::assert_ok;
+
+    #[test]
+    fn test_leave_role_later() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_leave_role_later::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_leave_role_immediatly() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_leave_role_immediatly::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_add_opening() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_add_opening::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_set_budget() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_set_budget::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_update_reward_account() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_update_reward_account::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_set_status_text() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_set_status_text::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_update_reward_amount() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_update_reward_amount::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_spend_from_budget() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_spend_from_budget::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_decrease_stake() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_decrease_stake::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_increase_stake() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_increase_stake::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_terminate_role_lead() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_terminate_role_lead::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_terminate_role_worker() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_terminate_role_worker::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_slash_stake() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_slash_stake::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_withdraw_application() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_withdraw_application::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_cancel_opening() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_cancel_opening::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_update_role_account() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_update_role_account::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_fill_opening_worker() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_fill_opening_worker::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_fill_opening_lead() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_fill_opening_lead::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_apply_on_opening() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_apply_on_opening::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_inintialize_rewarding_without_missing_reward() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_rewarding_without_missing_reward::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_inintialize_rewarding_with_missing_reward_cant_pay() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(
+                test_benchmark_on_initialize_rewarding_with_missing_reward_cant_pay::<Test>()
+            );
+        });
+    }
+
+    #[test]
+    fn test_on_inintialize_rewarding_with_missing_reward() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_rewarding_with_missing_reward::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_inintialize_leaving() {
+        build_test_externalities().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_leaving::<Test>());
+        });
+    }
+}

+ 260 - 0
runtime-modules/working-group/src/checks.rs

@@ -0,0 +1,260 @@
+use crate::{
+    ApplicationId, BalanceOf, Instance, MemberId, Opening, OpeningId, OpeningType, RewardPolicy,
+    StakePolicy, Trait, Worker, WorkerId,
+};
+
+use super::Error;
+use frame_support::dispatch::{DispatchError, DispatchResult};
+use frame_support::traits::Get;
+use frame_support::{ensure, StorageMap, StorageValue};
+use frame_system::{ensure_root, ensure_signed};
+use sp_arithmetic::traits::Zero;
+use sp_std::collections::btree_set::BTreeSet;
+use sp_std::marker::PhantomData;
+use sp_std::vec::Vec;
+
+use crate::types::{ApplicationInfo, StakeParameters};
+
+// Check opening: verifies origin and opening type compatibility.
+pub(crate) fn ensure_origin_for_opening_type<T: Trait<I>, I: Instance>(
+    origin: T::Origin,
+    opening_type: OpeningType,
+) -> DispatchResult {
+    match opening_type {
+        OpeningType::Regular => {
+            // Ensure lead is set and is origin signer.
+            ensure_origin_is_active_leader::<T, I>(origin)
+        }
+        OpeningType::Leader => {
+            // Council proposal.
+            ensure_root(origin).map_err(|err| err.into())
+        }
+    }
+}
+
+// Check opening: returns the opening by id if it is exists.
+pub(crate) fn ensure_opening_exists<T: Trait<I>, I: Instance>(
+    opening_id: &OpeningId,
+) -> Result<Opening<T::BlockNumber, BalanceOf<T>>, Error<T, I>> {
+    ensure!(
+        <crate::OpeningById::<T, I>>::contains_key(opening_id),
+        Error::<T, I>::OpeningDoesNotExist
+    );
+
+    let opening = <crate::OpeningById<T, I>>::get(opening_id);
+
+    Ok(opening)
+}
+
+// Check application: returns applicationId and application tuple if exists.
+pub(crate) fn ensure_application_exists<T: Trait<I>, I: Instance>(
+    application_id: &ApplicationId,
+) -> Result<ApplicationInfo<T, I>, Error<T, I>> {
+    ensure!(
+        <crate::ApplicationById::<T, I>>::contains_key(application_id),
+        Error::<T, I>::WorkerApplicationDoesNotExist
+    );
+
+    let application = <crate::ApplicationById<T, I>>::get(application_id);
+
+    Ok(ApplicationInfo {
+        application_id: *application_id,
+        application,
+        marker: PhantomData,
+    })
+}
+
+// Check application: returns applicationId and application tuple if exists.
+pub(crate) fn ensure_succesful_applications_exist<T: Trait<I>, I: Instance>(
+    successful_application_ids: &BTreeSet<ApplicationId>,
+) -> Result<Vec<ApplicationInfo<T, I>>, Error<T, I>> {
+    // Check for non-empty set of application ids.
+    ensure!(
+        !successful_application_ids.is_empty(),
+        crate::Error::<T, I>::NoApplicationsProvided
+    );
+
+    // Make iterator over successful worker application
+    let application_info_iterator = successful_application_ids
+        .iter()
+        // recover worker application from id
+        .map(|application_id| ensure_application_exists::<T, I>(application_id))
+        // remove Err cases, i.e. non-existing applications
+        .filter_map(|result| result.ok());
+
+    // Count number of successful workers provided.
+    let num_provided_successful_application_ids = successful_application_ids.len();
+
+    // Ensure all worker applications exist.
+    let number_of_successful_applications = application_info_iterator.clone().count();
+
+    ensure!(
+        number_of_successful_applications == num_provided_successful_application_ids,
+        crate::Error::<T, I>::SuccessfulWorkerApplicationDoesNotExist
+    );
+
+    let result_applications_info = application_info_iterator.collect::<Vec<_>>();
+
+    Ok(result_applications_info)
+}
+
+// Check leader: ensures that group leader was hired.
+pub(crate) fn ensure_lead_is_set<T: Trait<I>, I: Instance>() -> Result<WorkerId<T>, Error<T, I>> {
+    let leader_worker_id = <crate::CurrentLead<T, I>>::get();
+
+    if let Some(leader_worker_id) = leader_worker_id {
+        Ok(leader_worker_id)
+    } else {
+        Err(Error::<T, I>::CurrentLeadNotSet)
+    }
+}
+
+// Check leader: verifies that provided lead account id belongs to the current working group leader.
+fn ensure_is_lead_account<T: Trait<I>, I: Instance>(
+    lead_account_id: T::AccountId,
+) -> DispatchResult {
+    let leader_worker_id = ensure_lead_is_set::<T, I>()?;
+
+    let leader = <crate::WorkerById<T, I>>::get(leader_worker_id);
+
+    if leader.role_account_id != lead_account_id {
+        return Err(Error::<T, I>::IsNotLeadAccount.into());
+    }
+
+    Ok(())
+}
+
+/// Check leader: ensures origin is signed by the leader.
+pub fn ensure_origin_is_active_leader<T: Trait<I>, I: Instance>(
+    origin: T::Origin,
+) -> DispatchResult {
+    // Ensure is signed
+    let signer = ensure_signed(origin)?;
+
+    ensure_is_lead_account::<T, I>(signer)
+}
+
+/// Check worker: ensures the worker was already created.
+pub fn ensure_worker_exists<T: Trait<I>, I: Instance>(
+    worker_id: &WorkerId<T>,
+) -> Result<Worker<T>, Error<T, I>> {
+    ensure!(
+        <crate::WorkerById::<T, I>>::contains_key(worker_id),
+        Error::<T, I>::WorkerDoesNotExist
+    );
+
+    let worker = <crate::WorkerById<T, I>>::get(worker_id);
+
+    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,
+    worker_id: &WorkerId<T>,
+) -> Result<Worker<T>, DispatchError> {
+    // Ensure that it is signed
+    let signer_account = ensure_signed(origin)?;
+
+    // Ensure that id corresponds to active worker
+    let worker = ensure_worker_exists::<T, I>(&worker_id)?;
+
+    // Ensure that signer is actually role account of worker
+    ensure!(
+        signer_account == worker.role_account_id,
+        Error::<T, I>::SignerIsNotWorkerRoleAccount
+    );
+
+    Ok(worker)
+}
+
+// Check worker: verifies proper origin for the worker operation. Returns whether the origin is sudo.
+pub(crate) fn ensure_origin_for_worker_operation<T: Trait<I>, I: Instance>(
+    origin: T::Origin,
+    worker_id: WorkerId<T>,
+) -> Result<bool, DispatchError> {
+    let leader_worker_id = ensure_lead_is_set::<T, I>()?;
+
+    let (worker_opening_type, is_sudo) = if leader_worker_id == worker_id {
+        (OpeningType::Leader, true)
+    } else {
+        (OpeningType::Regular, false)
+    };
+
+    ensure_origin_for_opening_type::<T, I>(origin, worker_opening_type)?;
+
+    Ok(is_sudo)
+}
+
+// Check opening: verifies stake policy for the opening.
+pub(crate) fn ensure_valid_stake_policy<T: Trait<I>, I: Instance>(
+    stake_policy: &Option<StakePolicy<T::BlockNumber, BalanceOf<T>>>,
+) -> Result<(), DispatchError> {
+    if let Some(stake_policy) = stake_policy {
+        ensure!(
+            stake_policy.stake_amount != Zero::zero(),
+            Error::<T, I>::CannotStakeZero
+        );
+
+        ensure!(
+            stake_policy.leaving_unstaking_period > T::MinUnstakingPeriodLimit::get(),
+            Error::<T, I>::UnstakingPeriodLessThanMinimum
+        );
+    }
+
+    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>>>,
+) -> Result<(), DispatchError> {
+    if let Some(reward_policy) = reward_policy {
+        ensure!(
+            reward_policy.reward_per_block != Zero::zero(),
+            Error::<T, I>::CannotRewardWithZero
+        )
+    }
+
+    Ok(())
+}
+
+// Check application: verifies that proposed stake is enough for the opening.
+pub(crate) fn ensure_application_stake_match_opening<T: Trait<I>, I: Instance>(
+    opening: &Opening<T::BlockNumber, BalanceOf<T>>,
+    stake_parameters: &Option<StakeParameters<T::AccountId, BalanceOf<T>>>,
+) -> DispatchResult {
+    let opening_stake_balance = opening
+        .stake_policy
+        .clone()
+        .unwrap_or_default()
+        .stake_amount;
+
+    let application_stake_balance = stake_parameters.clone().unwrap_or_default().stake;
+
+    if application_stake_balance < opening_stake_balance {
+        return Err(Error::<T, I>::ApplicationStakeDoesntMatchOpening.into());
+    }
+
+    Ok(())
+}
+
+// Check worker: verifies that worker has recurring rewards.
+pub(crate) fn ensure_worker_has_recurring_reward<T: Trait<I>, I: Instance>(
+    worker: &Worker<T>,
+) -> DispatchResult {
+    worker
+        .reward_per_block
+        .map_or(Err(Error::<T, I>::WorkerHasNoReward.into()), |_| Ok(()))
+}

+ 45 - 639
runtime-modules/working-group/src/errors.rs

@@ -9,676 +9,82 @@ decl_error! {
         /// Provided stake balance cannot be zero.
         StakeBalanceCannotBeZero,
 
-        /// Cannot get the worker stake profile.
-        NoWorkerStakeProfile,
-
-        /// Current lead is not set.
-        CurrentLeadNotSet,
-
-        /// There is leader already, cannot hire another one.
-        CannotHireLeaderWhenLeaderExists,
-
-        /// Cannot fill opening with multiple applications.
-        CannotHireMultipleLeaders,
-
-        /// Not a lead account.
-        IsNotLeadAccount,
-
-        /// Opening text too short.
-        OpeningTextTooShort,
-
-        /// Opening text too long.
-        OpeningTextTooLong,
-
         /// Opening does not exist.
         OpeningDoesNotExist,
 
-        /// Insufficient balance to apply.
-        InsufficientBalanceToApply,
-
-        /// Unsigned origin.
-        MembershipUnsignedOrigin,
-
-        /// Member id is invalid.
-        MembershipInvalidMemberId,
-
-        /// Signer does not match controller account.
-        ApplyOnWorkerOpeningSignerNotControllerAccount,
-
-        /// Origin must be controller or root account of member.
-        OriginIsNeitherMemberControllerOrRoot,
-
-        /// Member already has an active application on the opening.
-        MemberHasActiveApplicationOnOpening,
-
-        /// Worker application text too long.
-        WorkerApplicationTextTooLong,
-
-        /// Worker application text too short.
-        WorkerApplicationTextTooShort,
-
-        /// Insufficient balance to cover stake.
-        InsufficientBalanceToCoverStake,
-
-        /// Origin is not applicant.
-        OriginIsNotApplicant,
+        /// Cannot fill opening with multiple applications.
+        CannotHireMultipleLeaders,
 
         /// Worker application does not exist.
         WorkerApplicationDoesNotExist,
 
+        /// Working group size limit exceeded.
+        MaxActiveWorkerNumberExceeded,
+
         /// Successful worker application does not exist.
         SuccessfulWorkerApplicationDoesNotExist,
 
-        /// Reward policy has invalid next payment block number.
-        FillOpeningInvalidNextPaymentBlock,
-
-        /// Working group mint does not exist.
-        FillOpeningMintDoesNotExist,
-
-        ///Relationship must exist.
-        RelationshipMustExist,
-
-        /// Worker exit rationale text is too long.
-        WorkerExitRationaleTextTooLong,
-
-        /// Worker exit rationale text is too short.
-        WorkerExitRationaleTextTooShort,
+        /// There is leader already, cannot hire another one.
+        CannotHireLeaderWhenLeaderExists,
 
-        /// Signer is not worker role account.
-        SignerIsNotWorkerRoleAccount,
+        /// Not a lead account.
+        IsNotLeadAccount,
 
-        /// Worker has no recurring reward.
-        WorkerHasNoReward,
+        /// Current lead is not set.
+        CurrentLeadNotSet,
 
         /// Worker does not exist.
         WorkerDoesNotExist,
 
-        /// Opening does not exist.
-        AcceptWorkerApplicationsOpeningDoesNotExist,
-
-        /// Opening Is Not in Waiting to begin.
-        AcceptWorkerApplicationsOpeningIsNotWaitingToBegin,
-
-        /// Opening does not exist.
-        BeginWorkerApplicantReviewOpeningDoesNotExist,
-
-        /// Opening Is Not in Waiting.
-        BeginWorkerApplicantReviewOpeningOpeningIsNotWaitingToBegin,
-
-        /// OpeningDoesNotExist.
-        FullWorkerOpeningOpeningDoesNotExist,
-
-        /// Opening not in review period stage.
-        FullWorkerOpeningOpeningNotInReviewPeriodStage,
-
-        /// Application stake unstaking period for successful applicants too short.
-        FullWorkerOpeningUnsuccessfulApplicationStakeUnstakingPeriodTooShort,
-
-        /// Application stake unstaking period for failed applicants too short.
-        FullWorkerOpeningSuccessfulApplicationStakeUnstakingPeriodTooShort,
-
-        /// Role stake unstaking period for successful applicants too short.
-        FullWorkerOpeningSuccessfulRoleStakeUnstakingPeriodTooShort,
-
-        /// Role stake unstaking period for failed applicants too short.
-        FullWorkerOpeningUnsuccessfulRoleStakeUnstakingPeriodTooShort,
-
-        /// Application stake unstaking period for successful applicants redundant.
-        FullWorkerOpeningSuccessfulApplicationStakeUnstakingPeriodRedundant,
-
-        /// Application stake unstaking period for failed applicants redundant.
-        FullWorkerOpeningUnsuccessfulApplicationStakeUnstakingPeriodRedundant,
-
-        /// Role stake unstaking period for successful applicants redundant.
-        FullWorkerOpeningSuccessfulRoleStakeUnstakingPeriodRedundant,
-
-        /// Role stake unstaking period for failed applicants redundant.
-        FullWorkerOpeningUnsuccessfulRoleStakeUnstakingPeriodRedundant,
-
-        /// Application does not exist.
-        FullWorkerOpeningApplicationDoesNotExist,
-
-        /// Application not in active stage.
-        FullWorkerOpeningApplicationNotActive,
-
-        /// Applications not for opening.
-        FillWorkerOpeningApplicationForWrongOpening,
-
-        /// Application does not exist.
-        WithdrawWorkerApplicationApplicationDoesNotExist,
-
-        /// Application is not active.
-        WithdrawWorkerApplicationApplicationNotActive,
-
-        /// Opening not accepting applications.
-        WithdrawWorkerApplicationOpeningNotAcceptingApplications,
-
-        /// UnstakingPeriodTooShort .... // <== SHOULD REALLY BE TWO SEPARATE, ONE FOR EACH STAKING PURPOSE
-        WithdrawWorkerApplicationUnstakingPeriodTooShort,
-
-        /// Redundant unstaking period provided
-        WithdrawWorkerApplicationRedundantUnstakingPeriod,
-
-        /// Opening does not activate in the future.
-        AddWorkerOpeningActivatesInThePast,
-
-        /// Role stake amount less than minimum currency balance.
-        AddWorkerOpeningRoleStakeLessThanMinimum,
-
-        /// Application stake amount less than minimum currency balance.
-        AddWorkerOpeningAppliicationStakeLessThanMinimum,
-
-        /// Opening does not exist.
-        AddWorkerOpeningOpeningDoesNotExist,
-
-        // <== SHOULD REALLY BE TWO SEPARATE, ONE FOR EACH STAKING PURPOSE
-        /// Stake provided when redundant.
-        AddWorkerOpeningStakeProvidedWhenRedundant,
-
-        // <== SHOULD REALLY BE TWO SEPARATE, ONE FOR EACH STAKING PURPOSE
-        /// Stake missing when required.
-        AddWorkerOpeningStakeMissingWhenRequired,
-
-        // <== SHOULD REALLY BE TWO SEPARATE, ONE FOR EACH STAKING PURPOSE
-        /// Stake amount too low.
-        AddWorkerOpeningStakeAmountTooLow,
+        /// Invalid origin for a member.
+        InvalidMemberOrigin,
 
-        /// Opening is not in accepting applications stage.
-        AddWorkerOpeningOpeningNotInAcceptingApplicationStage,
-
-        /// New application was crowded out.
-        AddWorkerOpeningNewApplicationWasCrowdedOut,
-
-        /// Application rationing has zero max active applicants.
-        AddWorkerOpeningZeroMaxApplicantCount,
-
-        /// Next payment is not in the future.
-        RecurringRewardsNextPaymentNotInFuture,
-
-        /// Recipient not found.
-        RecurringRewardsRecipientNotFound,
-
-        /// Recipient reward source not found.
-        RecurringRewardsRewardSourceNotFound,
-
-        /// Reward relationship not found.
-        RecurringRewardsRewardRelationshipNotFound,
-
-        /// Stake not found.
-        StakingErrorStakeNotFound,
-
-        /// Unstaking period should be greater than zero.
-        StakingErrorUnstakingPeriodShouldBeGreaterThanZero,
-
-        /// Already unstaking.
-        StakingErrorAlreadyUnstaking,
-
-        /// Not staked.
-        StakingErrorNotStaked,
-
-        /// Cannot unstake while slashes ongoing.
-        StakingErrorCannotUnstakeWhileSlashesOngoing,
-
-        /// Insufficient balance in source account.
-        StakingErrorInsufficientBalanceInSourceAccount,
-
-        /// Cannot change stake by zero.
-        StakingErrorCannotChangeStakeByZero,
-
-        /// Cannot increase stake while unstaking.
-        StakingErrorCannotIncreaseStakeWhileUnstaking,
-
-        /// Cannot decrease stake while slashes ongoing.
-        StakingErrorCannotDecreaseWhileSlashesOngoing,
-
-        /// Insufficient stake to decrease.
-        StakingErrorInsufficientStake,
-
-        /// Slash amount should be greater than zero.
-        StakingErrorSlashAmountShouldBeGreaterThanZero,
-
-        /// Cannot find mint in the minting module.
-        CannotFindMint,
-
-        /// Require root origin in extrinsics.
-        RequireRootOrigin,
-
-        /// Require signed origin in extrinsics.
-        RequireSignedOrigin,
-
-        /// Working group size limit exceeded.
-        MaxActiveWorkerNumberExceeded,
-
-        /// Add worker opening role stake cannot be zero.
-        AddWorkerOpeningRoleStakeCannotBeZero,
-
-        /// Add worker opening application stake cannot be zero.
-        AddWorkerOpeningApplicationStakeCannotBeZero,
-
-        /// Invalid OpeningPolicyCommitment parameter:
-        /// fill_opening_failed_applicant_application_stake_unstaking_period should be non-zero.
-        FillOpeningFailedApplicantApplicationStakeUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter:
-        /// fill_opening_failed_applicant_role_stake_unstaking_period should be non-zero.
-        FillOpeningFailedApplicantRoleStakeUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter:
-        /// fill_opening_successful_applicant_application_stake_unstaking_period should be non-zero.
-        FillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter:
-        /// exit_role_stake_unstaking_period should be non-zero.
-        ExitRoleStakeUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter:
-        /// exit_role_application_stake_unstaking_period should be non-zero.
-        ExitRoleApplicationStakeUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter:
-        /// terminate_role_stake_unstaking_period should be non-zero.
-        TerminateRoleStakeUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter:
-        /// terminate_application_stake_unstaking_period should be non-zero.
-        TerminateApplicationStakeUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter (role_staking_policy):
-        /// crowded_out_unstaking_period_length should be non-zero.
-        RoleStakingPolicyCrowdedOutUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter (role_staking_policy):
-        /// review_period_expired_unstaking_period_length should be non-zero.
-        RoleStakingPolicyReviewPeriodUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter (application_staking_policy):
-        /// crowded_out_unstaking_period_length should be non-zero.
-        ApplicationStakingPolicyCrowdedOutUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter (application_staking_policy):
-        /// review_period_expired_unstaking_period_length should be non-zero.
-        ApplicationStakingPolicyReviewPeriodUnstakingPeriodIsZero,
-
-        /// Invalid OpeningPolicyCommitment parameter (application_rationing_policy):
-        /// max_active_applicants should be non-zero.
-        ApplicationRationingPolicyMaxActiveApplicantsIsZero,
-
-        /// Minting error: NextAdjustmentInPast
-        MintingErrorNextAdjustmentInPast,
-    }
-}
+        /// Signer is not worker role account.
+        SignerIsNotWorkerRoleAccount,
 
-/// Error wrapper for external module error conversions.
-pub struct WrappedError<E> {
-    /// Generic error.
-    pub error: E,
-}
+        /// Cannot stake zero.
+        CannotStakeZero,
 
-/// Helps with conversion of other modules errors.
-#[macro_export]
-macro_rules! ensure_on_wrapped_error {
-    ($call:expr) => {{
-        { $call }
-            .map_err(|err| crate::WrappedError { error: err })
-            .map_err(|err| {
-                let e: Error<T, I> = err.into();
+        /// Insufficient balance to cover stake.
+        InsufficientBalanceToCoverStake,
 
-                e
-            })
-    }};
-}
+        /// Application stake is less than required opening stake.
+        ApplicationStakeDoesntMatchOpening,
 
-impl<T: Trait<I>, I: Instance>
-    sp_std::convert::From<WrappedError<hiring::BeginAcceptingApplicationsError>> for Error<T, I>
-{
-    fn from(wrapper: WrappedError<hiring::BeginAcceptingApplicationsError>) -> Self {
-        match wrapper.error {
-            hiring::BeginAcceptingApplicationsError::OpeningDoesNotExist => {
-                Error::AcceptWorkerApplicationsOpeningDoesNotExist
-            }
-            hiring::BeginAcceptingApplicationsError::OpeningIsNotInWaitingToBeginStage => {
-                Error::AcceptWorkerApplicationsOpeningIsNotWaitingToBegin
-            }
-        }
-    }
-}
+        /// Origin is not applicant.
+        OriginIsNotApplicant,
 
-impl<T: Trait<I>, I: Instance> sp_std::convert::From<WrappedError<hiring::AddOpeningError>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<hiring::AddOpeningError>) -> Self {
-        match wrapper.error {
-            hiring::AddOpeningError::OpeningMustActivateInTheFuture => {
-                Error::AddWorkerOpeningActivatesInThePast
-            }
-            hiring::AddOpeningError::StakeAmountLessThanMinimumStakeBalance(purpose) => {
-                match purpose {
-                    hiring::StakePurpose::Role => Error::AddWorkerOpeningRoleStakeLessThanMinimum,
-                    hiring::StakePurpose::Application => {
-                        Error::AddWorkerOpeningAppliicationStakeLessThanMinimum
-                    }
-                }
-            }
-            hiring::AddOpeningError::ApplicationRationingZeroMaxApplicants => {
-                Error::AddWorkerOpeningZeroMaxApplicantCount
-            }
-            hiring::AddOpeningError::StakeAmountCannotBeZero(purpose) => match purpose {
-                hiring::StakePurpose::Role => Error::AddWorkerOpeningRoleStakeCannotBeZero,
-                hiring::StakePurpose::Application => {
-                    Error::AddWorkerOpeningApplicationStakeCannotBeZero
-                }
-            },
-        }
-    }
-}
+        /// Invalid operation - worker is leaving.
+        WorkerIsLeaving,
 
-impl<T: Trait<I>, I: Instance> sp_std::convert::From<WrappedError<hiring::BeginReviewError>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<hiring::BeginReviewError>) -> Self {
-        match wrapper.error {
-            hiring::BeginReviewError::OpeningDoesNotExist => {
-                Error::BeginWorkerApplicantReviewOpeningDoesNotExist
-            }
-            hiring::BeginReviewError::OpeningNotInAcceptingApplicationsStage => {
-                Error::BeginWorkerApplicantReviewOpeningOpeningIsNotWaitingToBegin
-            }
-        }
-    }
-}
+        /// Reward could not be zero.
+        CannotRewardWithZero,
 
-impl<T: Trait<I>, I: Instance> sp_std::convert::From<WrappedError<hiring::FillOpeningError<T>>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<hiring::FillOpeningError<T>>) -> Self {
-        match wrapper.error {
-            hiring::FillOpeningError::<T>::OpeningDoesNotExist => {
-                Error::FullWorkerOpeningOpeningDoesNotExist
-            }
-            hiring::FillOpeningError::<T>::OpeningNotInReviewPeriodStage => {
-                Error::FullWorkerOpeningOpeningNotInReviewPeriodStage
-            }
-            hiring::FillOpeningError::<T>::UnstakingPeriodTooShort(
-                stake_purpose,
-                outcome_in_filled_opening,
-            ) => match stake_purpose {
-                hiring::StakePurpose::Application => match outcome_in_filled_opening {
-                    hiring::ApplicationOutcomeInFilledOpening::Success => {
-                        Error::FullWorkerOpeningSuccessfulApplicationStakeUnstakingPeriodTooShort
-                    }
-                    hiring::ApplicationOutcomeInFilledOpening::Failure => {
-                        Error::FullWorkerOpeningUnsuccessfulApplicationStakeUnstakingPeriodTooShort
-                    }
-                },
-                hiring::StakePurpose::Role => match outcome_in_filled_opening {
-                    hiring::ApplicationOutcomeInFilledOpening::Success => {
-                        Error::FullWorkerOpeningSuccessfulRoleStakeUnstakingPeriodTooShort
-                    }
-                    hiring::ApplicationOutcomeInFilledOpening::Failure => {
-                        Error::FullWorkerOpeningUnsuccessfulRoleStakeUnstakingPeriodTooShort
-                    }
-                },
-            },
-            hiring::FillOpeningError::<T>::RedundantUnstakingPeriodProvided(
-                stake_purpose,
-                outcome_in_filled_opening,
-            ) => match stake_purpose {
-                hiring::StakePurpose::Application => match outcome_in_filled_opening {
-                    hiring::ApplicationOutcomeInFilledOpening::Success => {
-                        Error::FullWorkerOpeningSuccessfulApplicationStakeUnstakingPeriodRedundant
-                    }
-                    hiring::ApplicationOutcomeInFilledOpening::Failure => {
-                        Error::FullWorkerOpeningUnsuccessfulApplicationStakeUnstakingPeriodRedundant
-                    }
-                },
-                hiring::StakePurpose::Role => match outcome_in_filled_opening {
-                    hiring::ApplicationOutcomeInFilledOpening::Success => {
-                        Error::FullWorkerOpeningSuccessfulRoleStakeUnstakingPeriodRedundant
-                    }
-                    hiring::ApplicationOutcomeInFilledOpening::Failure => {
-                        Error::FullWorkerOpeningUnsuccessfulRoleStakeUnstakingPeriodRedundant
-                    }
-                },
-            },
-            hiring::FillOpeningError::<T>::ApplicationDoesNotExist(_application_id) => {
-                Error::FullWorkerOpeningApplicationDoesNotExist
-            }
-            hiring::FillOpeningError::<T>::ApplicationNotInActiveStage(_application_id) => {
-                Error::FullWorkerOpeningApplicationNotActive
-            }
-            hiring::FillOpeningError::<T>::ApplicationForWrongOpening(_application_id) => {
-                Error::FillWorkerOpeningApplicationForWrongOpening
-            }
-        }
-    }
-}
+        /// Staking account doesn't belong to a member.
+        InvalidStakingAccountForMember,
 
-impl<T: Trait<I>, I: Instance>
-    sp_std::convert::From<WrappedError<hiring::DeactivateApplicationError>> for Error<T, I>
-{
-    fn from(wrapper: WrappedError<hiring::DeactivateApplicationError>) -> Self {
-        match wrapper.error {
-            hiring::DeactivateApplicationError::ApplicationDoesNotExist => {
-                Error::WithdrawWorkerApplicationApplicationDoesNotExist
-            }
-            hiring::DeactivateApplicationError::ApplicationNotActive => {
-                Error::WithdrawWorkerApplicationApplicationNotActive
-            }
-            hiring::DeactivateApplicationError::OpeningNotAcceptingApplications => {
-                Error::WithdrawWorkerApplicationOpeningNotAcceptingApplications
-            }
-            hiring::DeactivateApplicationError::UnstakingPeriodTooShort(_stake_purpose) => {
-                Error::WithdrawWorkerApplicationUnstakingPeriodTooShort
-            }
-            hiring::DeactivateApplicationError::RedundantUnstakingPeriodProvided(
-                _stake_purpose,
-            ) => Error::WithdrawWorkerApplicationRedundantUnstakingPeriod,
-        }
-    }
-}
+        /// Staking account contains conflicting stakes.
+        ConflictStakesOnAccount,
 
-impl<T: Trait<I>, I: Instance> sp_std::convert::From<WrappedError<hiring::AddApplicationError>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<hiring::AddApplicationError>) -> Self {
-        match wrapper.error {
-            hiring::AddApplicationError::OpeningDoesNotExist => {
-                Error::AddWorkerOpeningOpeningDoesNotExist
-            }
-            hiring::AddApplicationError::StakeProvidedWhenRedundant(_stake_purpose) => {
-                Error::AddWorkerOpeningStakeProvidedWhenRedundant
-            }
-            hiring::AddApplicationError::StakeMissingWhenRequired(_stake_purpose) => {
-                Error::AddWorkerOpeningStakeMissingWhenRequired
-            }
-            hiring::AddApplicationError::StakeAmountTooLow(_stake_purpose) => {
-                Error::AddWorkerOpeningStakeAmountTooLow
-            }
-            hiring::AddApplicationError::OpeningNotInAcceptingApplicationsStage => {
-                Error::AddWorkerOpeningOpeningNotInAcceptingApplicationStage
-            }
-            hiring::AddApplicationError::NewApplicationWasCrowdedOut => {
-                Error::AddWorkerOpeningNewApplicationWasCrowdedOut
-            }
-        }
-    }
-}
+        /// Worker has no recurring reward.
+        WorkerHasNoReward,
 
-impl<T: Trait<I>, I: Instance>
-    sp_std::convert::From<WrappedError<membership::MemberControllerAccountDidNotSign>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<membership::MemberControllerAccountDidNotSign>) -> Self {
-        match wrapper.error {
-            membership::MemberControllerAccountDidNotSign::UnsignedOrigin => {
-                Error::MembershipUnsignedOrigin
-            }
-            membership::MemberControllerAccountDidNotSign::MemberIdInvalid => {
-                Error::MembershipInvalidMemberId
-            }
-            membership::MemberControllerAccountDidNotSign::SignerControllerAccountMismatch => {
-                Error::ApplyOnWorkerOpeningSignerNotControllerAccount
-            }
-        }
-    }
-}
+        /// Specified unstaking period is less then minimum set for the group.
+        UnstakingPeriodLessThanMinimum,
 
-impl<T: Trait<I>, I: Instance> sp_std::convert::From<WrappedError<recurringrewards::RewardsError>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<recurringrewards::RewardsError>) -> Self {
-        match wrapper.error {
-            recurringrewards::RewardsError::NextPaymentNotInFuture => {
-                Error::RecurringRewardsNextPaymentNotInFuture
-            }
-            recurringrewards::RewardsError::RecipientNotFound => {
-                Error::RecurringRewardsRecipientNotFound
-            }
-            recurringrewards::RewardsError::RewardSourceNotFound => {
-                Error::RecurringRewardsRewardSourceNotFound
-            }
-            recurringrewards::RewardsError::RewardRelationshipNotFound => {
-                Error::RecurringRewardsRewardRelationshipNotFound
-            }
-        }
-    }
-}
+        /// Requested operation with stake is impossible because of stake was not defined for the role.
+        CannotChangeStakeWithoutStakingAccount,
 
-impl<T: Trait<I>, I: Instance>
-    sp_std::convert::From<WrappedError<stake::StakeActionError<stake::InitiateUnstakingError>>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<stake::StakeActionError<stake::InitiateUnstakingError>>) -> Self {
-        match wrapper.error {
-            stake::StakeActionError::StakeNotFound => Error::StakingErrorStakeNotFound,
-            stake::StakeActionError::Error(initiate_unstaking_error) => {
-                match initiate_unstaking_error {
-                    stake::InitiateUnstakingError::UnstakingPeriodShouldBeGreaterThanZero => {
-                        Error::StakingErrorUnstakingPeriodShouldBeGreaterThanZero
-                    }
-                    stake::InitiateUnstakingError::UnstakingError(unstaking_error) => {
-                        match unstaking_error {
-                            stake::UnstakingError::AlreadyUnstaking => {
-                                Error::StakingErrorAlreadyUnstaking
-                            }
-                            stake::UnstakingError::NotStaked => Error::StakingErrorNotStaked,
-                            stake::UnstakingError::CannotUnstakeWhileSlashesOngoing => {
-                                Error::StakingErrorCannotUnstakeWhileSlashesOngoing
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
+        /// Invalid spending amount.
+        CannotSpendZero,
 
-impl<T: Trait<I>, I: Instance>
-    sp_std::convert::From<
-        WrappedError<stake::StakeActionError<stake::IncreasingStakeFromAccountError>>,
-    > for Error<T, I>
-{
-    fn from(
-        wrapper: WrappedError<stake::StakeActionError<stake::IncreasingStakeFromAccountError>>,
-    ) -> Self {
-        match wrapper.error {
-            stake::StakeActionError::StakeNotFound => Error::StakingErrorStakeNotFound,
-            stake::StakeActionError::Error(increase_stake_error_from_account) => {
-                match increase_stake_error_from_account {
-                    stake::IncreasingStakeFromAccountError::InsufficientBalanceInSourceAccount => {
-                        Error::StakingErrorInsufficientBalanceInSourceAccount
-                    }
-                    stake::IncreasingStakeFromAccountError::IncreasingStakeError(
-                        increasing_stake_error,
-                    ) => match increasing_stake_error {
-                        stake::IncreasingStakeError::NotStaked => Error::StakingErrorNotStaked,
-                        stake::IncreasingStakeError::CannotChangeStakeByZero => {
-                            Error::StakingErrorCannotChangeStakeByZero
-                        }
-                        stake::IncreasingStakeError::CannotIncreaseStakeWhileUnstaking => {
-                            Error::StakingErrorCannotIncreaseStakeWhileUnstaking
-                        }
-                    },
-                }
-            }
-        }
-    }
-}
+        /// It's not enough budget for this spending.
+        InsufficientBudgetForSpending,
 
-impl<T: Trait<I>, I: Instance>
-    sp_std::convert::From<WrappedError<stake::StakeActionError<stake::IncreasingStakeError>>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<stake::StakeActionError<stake::IncreasingStakeError>>) -> Self {
-        match wrapper.error {
-            stake::StakeActionError::StakeNotFound => Error::StakingErrorStakeNotFound,
-            stake::StakeActionError::Error(increasing_stake_error) => {
-                match increasing_stake_error {
-                    stake::IncreasingStakeError::NotStaked => Error::StakingErrorNotStaked,
-                    stake::IncreasingStakeError::CannotChangeStakeByZero => {
-                        Error::StakingErrorCannotChangeStakeByZero
-                    }
-                    stake::IncreasingStakeError::CannotIncreaseStakeWhileUnstaking => {
-                        Error::StakingErrorCannotIncreaseStakeWhileUnstaking
-                    }
-                }
-            }
-        }
-    }
-}
-
-impl<T: Trait<I>, I: Instance>
-    sp_std::convert::From<WrappedError<stake::StakeActionError<stake::DecreasingStakeError>>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<stake::StakeActionError<stake::DecreasingStakeError>>) -> Self {
-        match wrapper.error {
-            stake::StakeActionError::StakeNotFound => Error::StakingErrorStakeNotFound,
-            stake::StakeActionError::Error(decreasing_stake_error) => {
-                match decreasing_stake_error {
-                    stake::DecreasingStakeError::NotStaked => Error::StakingErrorNotStaked,
-                    stake::DecreasingStakeError::CannotChangeStakeByZero => {
-                        Error::StakingErrorCannotChangeStakeByZero
-                    }
-                    stake::DecreasingStakeError::CannotDecreaseStakeWhileUnstaking => {
-                        Error::StakingErrorCannotIncreaseStakeWhileUnstaking
-                    }
-                    stake::DecreasingStakeError::CannotDecreaseStakeWhileOngoingSlahes => {
-                        Error::StakingErrorCannotDecreaseWhileSlashesOngoing
-                    }
-                    stake::DecreasingStakeError::InsufficientStake => {
-                        Error::StakingErrorInsufficientStake
-                    }
-                }
-            }
-        }
-    }
-}
-
-impl<T: Trait<I>, I: Instance>
-    sp_std::convert::From<WrappedError<stake::StakeActionError<stake::ImmediateSlashingError>>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<stake::StakeActionError<stake::ImmediateSlashingError>>) -> Self {
-        match wrapper.error {
-            stake::StakeActionError::StakeNotFound => Error::StakingErrorStakeNotFound,
-            stake::StakeActionError::Error(slashing_error) => match slashing_error {
-                stake::ImmediateSlashingError::NotStaked => Error::StakingErrorNotStaked,
-                stake::ImmediateSlashingError::SlashAmountShouldBeGreaterThanZero => {
-                    Error::StakingErrorSlashAmountShouldBeGreaterThanZero
-                }
-            },
-        }
-    }
-}
+        /// Cannot fill opening - no applications provided.
+        NoApplicationsProvided,
 
-impl<T: Trait<I>, I: Instance> sp_std::convert::From<WrappedError<minting::GeneralError>>
-    for Error<T, I>
-{
-    fn from(wrapper: WrappedError<minting::GeneralError>) -> Self {
-        match wrapper.error {
-            minting::GeneralError::MintNotFound => Error::CannotFindMint,
-            minting::GeneralError::NextAdjustmentInPast => Error::MintingErrorNextAdjustmentInPast,
-        }
+        /// Cannot decrease stake - stake delta greater than initial stake.
+        CannotDecreaseStakeDeltaGreaterThanStake,
     }
 }

File diff suppressed because it is too large
+ 421 - 476
runtime-modules/working-group/src/lib.rs


File diff suppressed because it is too large
+ 609 - 347
runtime-modules/working-group/src/tests/fixtures.rs


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

@@ -2,16 +2,15 @@ use frame_support::dispatch::{DispatchError, DispatchResult};
 use frame_system::RawOrigin;
 
 use crate::tests::fixtures::{
-    create_mint, increase_total_balance_issuance_using_account_id, set_mint_id, setup_members,
-    AddWorkerOpeningFixture, ApplyOnWorkerOpeningFixture, BeginReviewWorkerApplicationsFixture,
-    FillWorkerOpeningFixture, SetLeadFixture,
+    setup_members, AddOpeningFixture, ApplyOnOpeningFixture, FillOpeningFixture, HireLeadFixture,
 };
 use crate::tests::mock::TestWorkingGroup;
-use crate::{OpeningPolicyCommitment, OpeningType, RewardPolicy};
+use crate::types::StakeParameters;
+use crate::{OpeningType, RewardPolicy, StakePolicy};
 
 #[derive(Clone)]
 struct HiringWorkflowApplication {
-    stake: Option<u64>,
+    stake_parameters: Option<StakeParameters<u64, u64>>,
     worker_handle: Vec<u8>,
     origin: RawOrigin<u64>,
     member_id: u64,
@@ -20,87 +19,93 @@ struct HiringWorkflowApplication {
 pub struct HiringWorkflow {
     opening_type: OpeningType,
     expected_result: DispatchResult,
-    role_stake: Option<u64>,
+    stake_policy: Option<StakePolicy<u64, u64>>,
+    reward_policy: Option<RewardPolicy<u64>>,
     applications: Vec<HiringWorkflowApplication>,
     setup_environment: bool,
-    reward_policy: Option<RewardPolicy<u64, u64>>,
 }
 
 impl Default for HiringWorkflow {
     fn default() -> Self {
         Self {
-            opening_type: OpeningType::Worker,
+            opening_type: OpeningType::Regular,
             expected_result: Ok(()),
-            role_stake: None,
+            stake_policy: None,
+            reward_policy: None,
             applications: Vec::new(),
             setup_environment: true,
-            reward_policy: None,
         }
     }
 }
 
 impl HiringWorkflow {
-    pub fn expect(self, result: DispatchResult) -> Self {
+    pub fn with_stake_policy(self, stake_policy: Option<StakePolicy<u64, u64>>) -> Self {
         Self {
-            expected_result: result,
+            stake_policy,
             ..self
         }
     }
 
-    pub fn disable_setup_environment(self) -> Self {
+    pub fn with_reward_policy(self, reward_policy: Option<RewardPolicy<u64>>) -> Self {
         Self {
-            setup_environment: false,
+            reward_policy,
             ..self
         }
     }
 
-    pub fn with_setup_environment(self, setup_environment: bool) -> Self {
+    pub fn expect(self, result: DispatchResult) -> Self {
         Self {
-            setup_environment,
+            expected_result: result,
             ..self
         }
     }
 
-    pub fn with_opening_type(self, opening_type: OpeningType) -> Self {
+    pub fn with_setup_environment(self, setup_environment: bool) -> Self {
         Self {
-            opening_type,
+            setup_environment,
             ..self
         }
     }
 
-    pub fn with_role_stake(self, role_stake: Option<u64>) -> Self {
-        Self { role_stake, ..self }
-    }
-
-    pub fn with_reward_policy(self, reward_policy: Option<RewardPolicy<u64, u64>>) -> Self {
+    pub fn with_opening_type(self, opening_type: OpeningType) -> Self {
         Self {
-            reward_policy,
+            opening_type,
             ..self
         }
     }
 
     pub fn add_default_application(self) -> Self {
-        let worker_handle = b"default worker handle".to_vec();
+        let worker_handle = b"default".to_vec();
 
         self.add_application(worker_handle)
     }
 
     pub fn add_application(self, worker_handle: Vec<u8>) -> Self {
-        self.add_application_with_origin(worker_handle, RawOrigin::Signed(1), 1)
+        self.add_application_full(worker_handle, RawOrigin::Signed(1), 1, Some(1))
     }
 
-    pub fn add_application_with_origin(
+    pub fn add_application_full(
         self,
         worker_handle: Vec<u8>,
         origin: RawOrigin<u64>,
         member_id: u64,
+        staking_account_id: Option<u64>,
     ) -> Self {
+        let stake_parameters = staking_account_id.map(|staking_account_id| StakeParameters {
+            stake: self
+                .stake_policy
+                .clone()
+                .map(|policy| policy.stake_amount)
+                .unwrap_or_default(),
+            staking_account_id,
+        });
+
         let mut applications = self.applications;
         applications.push(HiringWorkflowApplication {
             worker_handle,
-            stake: self.role_stake.clone(),
             origin,
             member_id,
+            stake_parameters,
         });
 
         Self {
@@ -110,12 +115,11 @@ impl HiringWorkflow {
     }
 
     fn setup_environment(&self) {
-        if matches!(self.opening_type, OpeningType::Worker) {
-            SetLeadFixture::default().set_lead();
+        if matches!(self.opening_type, OpeningType::Regular) {
+            HireLeadFixture::default().hire_lead();
+        } else {
+            setup_members(6);
         }
-        increase_total_balance_issuance_using_account_id(1, 10000);
-        setup_members(4);
-        set_mint_id(create_mint());
     }
 
     pub fn execute(&self) -> Option<u64> {
@@ -135,7 +139,7 @@ impl HiringWorkflow {
     fn fill_worker_position(&self) -> Result<u64, DispatchError> {
         let origin = match self.opening_type {
             OpeningType::Leader => RawOrigin::Root,
-            OpeningType::Worker => {
+            OpeningType::Regular => {
                 let leader_worker_id = TestWorkingGroup::current_lead().unwrap();
                 let leader = TestWorkingGroup::worker_by_id(leader_worker_id);
                 let lead_account_id = leader.role_account_id;
@@ -145,56 +149,32 @@ impl HiringWorkflow {
         };
 
         // create the opening
-        let mut add_worker_opening_fixture = AddWorkerOpeningFixture::default()
+        let add_worker_opening_fixture = AddOpeningFixture::default()
+            .with_stake_policy(self.stake_policy.clone())
+            .with_reward_policy(self.reward_policy.clone())
             .with_opening_type(self.opening_type)
             .with_origin(origin.clone());
 
-        if let Some(stake) = self.role_stake.clone() {
-            add_worker_opening_fixture =
-                add_worker_opening_fixture.with_policy_commitment(OpeningPolicyCommitment {
-                    role_staking_policy: Some(hiring::StakingPolicy {
-                        amount: stake,
-                        amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                        crowded_out_unstaking_period_length: None,
-                        review_period_expired_unstaking_period_length: None,
-                    }),
-                    ..OpeningPolicyCommitment::default()
-                });
-        }
-
         let opening_id = add_worker_opening_fixture.call()?;
 
         // Fill applications.
         let mut application_ids = Vec::new();
         for application in self.applications.clone() {
             let apply_on_worker_opening_fixture =
-                ApplyOnWorkerOpeningFixture::default_for_opening_id(opening_id)
+                ApplyOnOpeningFixture::default_for_opening_id(opening_id)
+                    .with_stake_parameters(application.stake_parameters)
                     .with_text(application.worker_handle)
-                    .with_origin(application.origin, application.member_id)
-                    .with_role_stake(self.role_stake);
+                    .with_origin(application.origin, application.member_id);
 
             let application_id = apply_on_worker_opening_fixture.call()?;
             application_ids.push(application_id);
         }
 
-        // begin application review
-
-        let begin_review_worker_applications_fixture =
-            BeginReviewWorkerApplicationsFixture::default_for_opening_id(opening_id)
-                .with_origin(origin.clone());
-        begin_review_worker_applications_fixture.call_and_assert(Ok(()));
-
         // fill opening
-        let mut fill_worker_opening_fixture =
-            FillWorkerOpeningFixture::default_for_ids(opening_id, application_ids)
-                .with_origin(origin.clone());
-
-        if let Some(reward_policy) = self.reward_policy.clone() {
-            fill_worker_opening_fixture =
-                fill_worker_opening_fixture.with_reward_policy(reward_policy);
-        }
+        let fill_opening_fixture = FillOpeningFixture::default_for_ids(opening_id, application_ids)
+            .with_origin(origin.clone());
 
-        let worker_id = fill_worker_opening_fixture.call()?;
+        let worker_id = fill_opening_fixture.call()?;
 
         Ok(worker_id)
     }

+ 34 - 108
runtime-modules/working-group/src/tests/mock.rs

@@ -1,4 +1,3 @@
-use frame_support::storage::StorageMap;
 use frame_support::traits::{OnFinalize, OnInitialize};
 use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
 use frame_system;
@@ -8,17 +7,14 @@ use sp_runtime::{
     traits::{BlakeTwo256, IdentityLookup},
     Perbill,
 };
-use std::marker::PhantomData;
 
-use crate::{BalanceOf, Module, NegativeImbalance, Trait};
-use common::constraints::InputValidationLengthConstraint;
+use crate::{DefaultInstance, Module, Trait};
 
 impl_outer_origin! {
     pub enum Origin for Test {}
 }
 
 mod working_group {
-    pub use super::TestWorkingGroupInstance;
     pub use crate::Event;
 }
 
@@ -29,7 +25,7 @@ mod membership_mod {
 impl_outer_event! {
     pub enum TestEvent for Test {
         balances<T>,
-        working_group TestWorkingGroupInstance <T>,
+        crate DefaultInstance <T>,
         membership_mod<T>,
         frame_system<T>,
     }
@@ -77,34 +73,6 @@ impl frame_system::Trait for Test {
     type SystemWeightInfo = ();
 }
 
-impl hiring::Trait for Test {
-    type OpeningId = u64;
-    type ApplicationId = u64;
-    type ApplicationDeactivatedHandler = ();
-    type StakeHandlerProvider = hiring::Module<Self>;
-}
-
-impl minting::Trait for Test {
-    type Currency = Balances;
-    type MintId = u64;
-}
-
-impl stake::Trait for Test {
-    type Currency = Balances;
-    type StakePoolId = StakePoolId;
-    type StakingEventsHandler = StakingEventsHandler<Test>;
-    type StakeId = u64;
-    type SlashId = u64;
-}
-
-impl membership::Trait for Test {
-    type Event = TestEvent;
-    type MemberId = u64;
-    type PaidTermId = u64;
-    type SubscriptionId = u64;
-    type ActorId = u64;
-}
-
 impl common::currency::GovernanceCurrency for Test {
     type Currency = Balances;
 }
@@ -126,102 +94,60 @@ impl balances::Trait for Test {
     type MaxLocks = ();
 }
 
-impl recurringrewards::Trait for Test {
-    type PayoutStatusHandler = ();
-    type RecipientId = u64;
-    type RewardRelationshipId = u64;
+impl membership::Trait for Test {
+    type Event = TestEvent;
+    type MemberId = u64;
+    type PaidTermId = u64;
+    type SubscriptionId = u64;
+    type ActorId = u64;
 }
 
+pub type Membership = membership::Module<Test>;
 pub type Balances = balances::Module<Test>;
 pub type System = frame_system::Module<Test>;
 
 parameter_types! {
+    pub const RewardPeriod: u32 = 2;
     pub const MaxWorkerNumberLimit: u32 = 3;
+    pub const MinUnstakingPeriodLimit: u64 = 3;
+    pub const LockId: [u8; 8] = [1; 8];
 }
 
-impl Trait<TestWorkingGroupInstance> for Test {
+impl Trait for Test {
     type Event = TestEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, LockId>;
+    type MemberOriginValidator = ();
+    type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
+    type RewardPeriod = RewardPeriod;
 }
 
-pub type Membership = membership::Module<Test>;
-
-pub type TestWorkingGroupInstance = crate::Instance1;
-pub type TestWorkingGroup = Module<Test, TestWorkingGroupInstance>;
+pub const ACTOR_ORIGIN_ERROR: &'static str = "Invalid membership";
 
-pub(crate) const WORKING_GROUP_MINT_CAPACITY: u64 = 40000;
-pub(crate) const WORKING_GROUP_CONSTRAINT_MIN: u16 = 1;
-pub(crate) const WORKING_GROUP_CONSTRAINT_DIFF: u16 = 40;
+impl common::origin::ActorOriginValidator<Origin, u64, u64> for () {
+    fn ensure_actor_origin(origin: Origin, member_id: u64) -> Result<u64, &'static str> {
+        let signed_account_id = frame_system::ensure_signed(origin)?;
 
-pub fn build_test_externalities() -> sp_io::TestExternalities {
-    let mut t = frame_system::GenesisConfig::default()
-        .build_storage::<Test>()
-        .unwrap();
+        if member_id > 10 {
+            return Err(ACTOR_ORIGIN_ERROR);
+        }
 
-    crate::GenesisConfig::<Test, TestWorkingGroupInstance> {
-        phantom: Default::default(),
-        working_group_mint_capacity: WORKING_GROUP_MINT_CAPACITY,
-        opening_human_readable_text_constraint: InputValidationLengthConstraint::new(
-            WORKING_GROUP_CONSTRAINT_MIN,
-            WORKING_GROUP_CONSTRAINT_DIFF,
-        ),
-        worker_application_human_readable_text_constraint: InputValidationLengthConstraint::new(
-            WORKING_GROUP_CONSTRAINT_MIN,
-            WORKING_GROUP_CONSTRAINT_DIFF,
-        ),
-        worker_exit_rationale_text_constraint: InputValidationLengthConstraint::new(
-            WORKING_GROUP_CONSTRAINT_MIN,
-            WORKING_GROUP_CONSTRAINT_DIFF,
-        ),
+        Ok(signed_account_id)
     }
-    .assimilate_storage(&mut t)
-    .unwrap();
-
-    t.into()
 }
 
-pub struct StakingEventsHandler<T> {
-    pub marker: PhantomData<T>,
-}
+pub type TestWorkingGroup = Module<Test, DefaultInstance>;
 
-impl<T: stake::Trait + crate::Trait<TestWorkingGroupInstance>> stake::StakingEventsHandler<T>
-    for StakingEventsHandler<T>
-{
-    /// Unstake remaining sum back to the source_account_id
-    fn unstaked(
-        stake_id: &<T as stake::Trait>::StakeId,
-        _unstaked_amount: BalanceOf<T>,
-        remaining_imbalance: NegativeImbalance<T>,
-    ) -> NegativeImbalance<T> {
-        // Stake not related to a staked role managed by the hiring module.
-        if !hiring::ApplicationIdByStakingId::<T>::contains_key(*stake_id) {
-            return remaining_imbalance;
-        }
-
-        let hiring_application_id = hiring::ApplicationIdByStakingId::<T>::get(*stake_id);
+pub const STAKING_ACCOUNT_ID_FOR_FAILED_VALIDITY_CHECK: u64 = 111;
+pub const STAKING_ACCOUNT_ID_FOR_CONFLICTING_STAKES: u64 = 333;
+pub const STAKING_ACCOUNT_ID_FOR_ZERO_STAKE: u64 = 444;
 
-        if crate::MemberIdByHiringApplicationId::<T, TestWorkingGroupInstance>::contains_key(
-            hiring_application_id,
-        ) {
-            return <crate::Module<T, TestWorkingGroupInstance>>::refund_working_group_stake(
-                *stake_id,
-                remaining_imbalance,
-            );
-        }
-
-        remaining_imbalance
-    }
+pub fn build_test_externalities() -> sp_io::TestExternalities {
+    let t = frame_system::GenesisConfig::default()
+        .build_storage::<Test>()
+        .unwrap();
 
-    /// Empty handler for slashing
-    fn slashed(
-        _: &<T as stake::Trait>::StakeId,
-        _: Option<<T as stake::Trait>::SlashId>,
-        _: BalanceOf<T>,
-        _: BalanceOf<T>,
-        remaining_imbalance: NegativeImbalance<T>,
-    ) -> NegativeImbalance<T> {
-        remaining_imbalance
-    }
+    t.into()
 }
 
 // Recommendation from Parity on testing on_finalize

File diff suppressed because it is too large
+ 422 - 418
runtime-modules/working-group/src/tests/mod.rs


+ 179 - 159
runtime-modules/working-group/src/types.rs

@@ -1,99 +1,78 @@
 #![warn(missing_docs)]
 
 use codec::{Decode, Encode};
-use sp_std::collections::btree_set::BTreeSet;
+use sp_std::vec::Vec;
 
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
+use sp_std::marker::PhantomData;
 
-/// Terms for slashes applied to a given role.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq)]
-pub struct SlashableTerms {
-    /// Maximum number of slashes.
-    pub max_count: u16,
+/// Working group job application type alias.
+pub type Application<T> = JobApplication<<T as frame_system::Trait>::AccountId, MemberId<T>>;
 
-    /// Maximum percentage points of remaining stake which may be slashed in a single slash.
-    pub max_percent_pts_per_time: u16,
-}
+/// Member identifier in membership::member module.
+pub type MemberId<T> = <T as membership::Trait>::MemberId;
 
-/// Terms for what slashing can be applied in some context.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq)]
-pub enum SlashingTerms {
-    /// Cannot be slashed.
-    Unslashable,
+/// Type identifier for a worker role, which must be same as membership actor identifier.
+pub type WorkerId<T> = <T as membership::Trait>::ActorId;
 
-    /// Can be slashed.
-    Slashable(SlashableTerms),
-}
+/// Type for an application id.
+pub type ApplicationId = u64;
 
-/// Must be default constructable because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl Default for SlashingTerms {
-    fn default() -> Self {
-        Self::Unslashable
-    }
-}
+/// Type for an opening id.
+pub type OpeningId = u64;
 
-/// A commitment to the set of policy variables relevant to an opening.
-/// An applicant can observe this commitment and be secure that the terms
-/// of the application process cannot be changed ex-post.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]
-pub struct OpeningPolicyCommitment<BlockNumber, Balance> {
-    /// Rationing to be used.
-    pub application_rationing_policy: Option<hiring::ApplicationRationingPolicy>,
-
-    /// Maximum length of review period of applications.
-    pub max_review_period_length: BlockNumber,
-
-    /// Staking policy for application.
-    pub application_staking_policy: Option<hiring::StakingPolicy<Balance, BlockNumber>>,
-
-    /// Staking policy for role itself.
-    pub role_staking_policy: Option<hiring::StakingPolicy<Balance, BlockNumber>>,
-
-    /// Slashing terms during role, NOT application itself!
-    pub role_slashing_terms: SlashingTerms,
-
-    /// When filling an opening: unstaking period for application stake of successful applicants.
-    pub fill_opening_successful_applicant_application_stake_unstaking_period: Option<BlockNumber>,
-
-    /// When filling an opening: unstaking period for the application stake of failed applicants.
-    pub fill_opening_failed_applicant_application_stake_unstaking_period: Option<BlockNumber>,
-
-    /// When filling an opening: unstaking period for the role stake of failed applicants.
-    pub fill_opening_failed_applicant_role_stake_unstaking_period: Option<BlockNumber>,
+// ApplicationId - Application - helper struct.
+pub(crate) struct ApplicationInfo<T: crate::Trait<I>, I: crate::Instance> {
+    pub application_id: ApplicationId,
+    pub application: Application<T>,
+    pub marker: PhantomData<I>,
+}
 
-    /// When terminating a worker: unstaking period for application stake.
-    pub terminate_application_stake_unstaking_period: Option<BlockNumber>,
+// WorkerId - Worker - helper struct.
+pub(crate) struct WorkerInfo<T: membership::Trait + frame_system::Trait + balances::Trait> {
+    pub worker_id: WorkerId<T>,
+    pub worker: Worker<T>,
+}
 
-    /// When terminating a worke/leadr: unstaking period for role stake.
-    pub terminate_role_stake_unstaking_period: Option<BlockNumber>,
+impl<T: membership::Trait + frame_system::Trait + balances::Trait> From<(WorkerId<T>, Worker<T>)>
+    for WorkerInfo<T>
+{
+    fn from((worker_id, worker): (WorkerId<T>, Worker<T>)) -> Self {
+        WorkerInfo { worker_id, worker }
+    }
+}
 
-    /// When a worker/lead exists: unstaking period for application stake.
-    pub exit_role_application_stake_unstaking_period: Option<BlockNumber>,
+/// Group worker type alias.
+pub type Worker<T> = GroupWorker<
+    <T as frame_system::Trait>::AccountId,
+    MemberId<T>,
+    <T as frame_system::Trait>::BlockNumber,
+    BalanceOf<T>,
+>;
 
-    /// When a worker/lead exists: unstaking period for role stake.
-    pub exit_role_stake_unstaking_period: Option<BlockNumber>,
-}
+/// Balance alias for `balances` module.
+pub type BalanceOf<T> = <T as balances::Trait>::Balance;
 
-/// An opening for a worker or lead role.
+/// Job opening for the normal or leader position.
+/// An opening represents the process of hiring one or more new actors into some available role.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct Opening<OpeningId, BlockNumber, Balance, WorkerApplicationId: core::cmp::Ord> {
-    /// Identifier for underlying opening in the hiring module.
-    pub hiring_opening_id: OpeningId,
+#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, Eq)]
+pub struct Opening<BlockNumber: Ord, Balance> {
+    /// Defines opening type: Leader or worker.
+    pub opening_type: OpeningType,
 
-    /// Set of identifiers for all worker applications ever added.
-    pub applications: BTreeSet<WorkerApplicationId>,
+    /// Block at which opening was added.
+    pub created: BlockNumber,
 
-    /// Commitment to policies in opening.
-    pub policy_commitment: OpeningPolicyCommitment<BlockNumber, Balance>,
+    /// Hash of the opening description.
+    pub description_hash: Vec<u8>,
 
-    /// Defines opening type: Leader or worker.
-    pub opening_type: OpeningType,
+    /// 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>>,
 }
 
 /// Defines type of the opening: regular working group fellow or group leader.
@@ -104,148 +83,189 @@ pub enum OpeningType {
     Leader,
 
     /// Regular worker.
-    Worker,
+    Regular,
 }
 
 /// 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 OpeningType {
     fn default() -> Self {
-        Self::Worker
+        Self::Regular
     }
 }
 
-/// An application for the worker/lead role.
+/// An application for the regular worker/lead role opening.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct Application<AccountId, OpeningId, MemberId, ApplicationId> {
+pub struct JobApplication<AccountId, MemberId> {
     /// Account used to authenticate in this role.
     pub role_account_id: AccountId,
 
-    /// Opening on which this application applies.
-    pub opening_id: OpeningId,
+    /// Reward account id.
+    pub reward_account_id: AccountId,
+
+    /// Account used to stake in this role.
+    pub staking_account_id: Option<AccountId>,
 
     /// Member applying.
     pub member_id: MemberId,
 
-    /// Underlying application in hiring module.
-    pub hiring_application_id: ApplicationId,
+    /// Hash of the application description.
+    pub description_hash: Vec<u8>,
 }
 
-impl<AccountId: Clone, OpeningId: Clone, MemberId: Clone, ApplicationId: Clone>
-    Application<AccountId, OpeningId, MemberId, ApplicationId>
-{
-    /// Creates a new worker application using parameters.
+impl<AccountId: Clone, MemberId: Clone> JobApplication<AccountId, MemberId> {
+    /// Creates a new job application using parameters.
     pub fn new(
         role_account_id: &AccountId,
-        opening_id: &OpeningId,
+        reward_account_id: &AccountId,
+        staking_account_id: &Option<AccountId>,
         member_id: &MemberId,
-        application_id: &ApplicationId,
+        description_hash: Vec<u8>,
     ) -> Self {
-        Application {
+        JobApplication {
             role_account_id: role_account_id.clone(),
-            opening_id: opening_id.clone(),
+            reward_account_id: reward_account_id.clone(),
+            staking_account_id: staking_account_id.clone(),
             member_id: member_id.clone(),
-            hiring_application_id: application_id.clone(),
+            description_hash,
         }
     }
 }
 
-/// Role stake information for a worker/lead.
+/// Working group participant: regular worker or lead.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct RoleStakeProfile<StakeId, BlockNumber> {
-    /// Whether participant is staked, and if so, the identifier for this staking in the staking module.
-    pub stake_id: StakeId,
-
-    /// Unstaking period when terminated.
-    pub termination_unstaking_period: Option<BlockNumber>,
-
-    /// Unstaking period when exiting.
-    pub exit_unstaking_period: Option<BlockNumber>,
-}
-
-impl<StakeId: Clone, BlockNumber: Clone> RoleStakeProfile<StakeId, BlockNumber> {
-    /// Creates a new worker/lead role stake profile using stake parameters.
-    pub fn new(
-        stake_id: &StakeId,
-        termination_unstaking_period: &Option<BlockNumber>,
-        exit_unstaking_period: &Option<BlockNumber>,
-    ) -> Self {
-        Self {
-            stake_id: stake_id.clone(),
-            termination_unstaking_period: termination_unstaking_period.clone(),
-            exit_unstaking_period: exit_unstaking_period.clone(),
-        }
-    }
-}
-
-/// Working group participant: worker/lead.
-/// This role can be staked, have reward and be inducted through the hiring module.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct Worker<AccountId, RewardRelationshipId, StakeId, BlockNumber, MemberId> {
+pub struct GroupWorker<AccountId, MemberId, BlockNumber, Balance> {
     /// Member id related to the worker/lead.
     pub member_id: MemberId,
 
     /// Account used to authenticate in this role.
     pub role_account_id: AccountId,
 
-    /// Whether the role has recurring reward, and if so an identifier for this.
-    pub reward_relationship: Option<RewardRelationshipId>,
+    /// Account used to stake in this role.
+    pub staking_account_id: Option<AccountId>,
+
+    /// Reward account id.
+    pub reward_account_id: AccountId,
+
+    /// Specifies the block when the worker chose to leave.
+    pub started_leaving_at: Option<BlockNumber>,
 
-    /// When set, describes role stake of the worker/lead.
-    pub role_stake_profile: Option<RoleStakeProfile<StakeId, BlockNumber>>,
+    /// Unstaking period when the worker chooses to leave the role.
+    /// It is defined by the job opening.
+    pub job_unstaking_period: BlockNumber,
+
+    /// Optional reward setting for the worker.
+    pub reward_per_block: Option<Balance>,
+
+    /// Total missed reward amount.
+    pub missed_reward: Option<Balance>,
+
+    /// Specifies the block when the worker was created.
+    pub created_at: BlockNumber,
 }
 
-impl<
-        AccountId: Clone,
-        RewardRelationshipId: Clone,
-        StakeId: Clone,
-        BlockNumber: Clone,
-        MemberId: Clone,
-    > Worker<AccountId, RewardRelationshipId, StakeId, BlockNumber, MemberId>
+impl<AccountId: Clone, MemberId: Clone, BlockNumber, Balance>
+    GroupWorker<AccountId, MemberId, BlockNumber, Balance>
 {
-    /// Creates a new _Worker_ using parameters.
+    /// Creates a new _GroupWorker_ using parameters.
     pub fn new(
         member_id: &MemberId,
         role_account_id: &AccountId,
-        reward_relationship: &Option<RewardRelationshipId>,
-        role_stake_profile: &Option<RoleStakeProfile<StakeId, BlockNumber>>,
+        reward_account_id: &AccountId,
+        staking_account_id: &Option<AccountId>,
+        job_unstaking_period: BlockNumber,
+        reward_per_block: Option<Balance>,
+        created_at: BlockNumber,
     ) -> Self {
-        Worker {
+        GroupWorker {
             member_id: member_id.clone(),
             role_account_id: role_account_id.clone(),
-            reward_relationship: reward_relationship.clone(),
-            role_stake_profile: role_stake_profile.clone(),
+            reward_account_id: reward_account_id.clone(),
+            staking_account_id: staking_account_id.clone(),
+            started_leaving_at: None,
+            job_unstaking_period,
+            reward_per_block,
+            missed_reward: None,
+            created_at,
         }
     }
+
+    /// Defines whether the worker is leaving the role.
+    pub fn is_leaving(&self) -> bool {
+        self.started_leaving_at.is_some()
+    }
+}
+
+/// 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,
 }
 
-/// Origin of exit initiation.
+/// Stake policy for the job opening.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub enum ExitInitiationOrigin {
-    /// Lead fires the worker.
-    Lead,
+#[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]
+pub struct StakePolicy<BlockNumber, Balance> {
+    /// Stake amount for applicants.
+    pub stake_amount: Balance,
+
+    /// Unstaking period for the stake. Zero means no unstaking period.
+    pub leaving_unstaking_period: BlockNumber,
+}
+
+/// Parameters container for the apply_on_opening extrinsic.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]
+pub struct ApplyOnOpeningParams<MemberId, OpeningId, AccountId, Balance> {
+    /// Applying member id.
+    pub member_id: MemberId,
+
+    /// Opening id to apply on.
+    pub opening_id: OpeningId,
 
-    /// Worker leaves the position.
-    Worker,
+    /// Role account id.
+    pub role_account_id: AccountId,
+
+    /// Reward account id.
+    pub reward_account_id: AccountId,
+
+    /// Application description.
+    pub description: Vec<u8>,
 
-    /// Council fires the leader.
-    Sudo,
+    /// Stake information for the application.
+    pub stake_parameters: Option<StakeParameters<AccountId, Balance>>,
 }
 
-/// The recurring reward if any to be assigned to an actor when filling in the position.
+/// Contains information for the stakes when applying for opening.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, PartialEq, Debug)]
-pub struct RewardPolicy<Balance, BlockNumber> {
-    /// Balance per payout.
-    pub amount_per_payout: Balance,
+#[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]
+pub struct StakeParameters<AccountId, Balance> {
+    /// Stake balance.
+    pub stake: Balance,
 
-    /// Next payment time (in blocks).
-    pub next_payment_at_block: BlockNumber,
+    /// Staking account id.
+    pub staking_account_id: AccountId,
+}
+
+/// ApplyOnOpeningParams type alias.
+pub type ApplyOnOpeningParameters<T> = ApplyOnOpeningParams<
+    MemberId<T>,
+    OpeningId,
+    <T as frame_system::Trait>::AccountId,
+    BalanceOf<T>,
+>;
+
+/// Contains information for the slashing.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]
+pub struct Penalty<Balance> {
+    /// Slashing rationale
+    pub slashing_text: Vec<u8>,
 
-    /// Optional payout interval.
-    pub payout_interval: Option<BlockNumber>,
+    /// Slashing balance
+    pub slashing_amount: Balance,
 }

+ 2 - 0
runtime/Cargo.toml

@@ -79,6 +79,7 @@ proposals-discussion = { package = 'pallet-proposals-discussion', default-featur
 proposals-codex = { package = 'pallet-proposals-codex', default-features = false, path = '../runtime-modules/proposals/codex'}
 content-directory = { package = 'pallet-content-directory', default-features = false, path = '../runtime-modules/content-directory' }
 pallet_constitution = { package = 'pallet-constitution', default-features = false, path = '../runtime-modules/constitution' }
+staking-handler = { package = 'staking-handler', default-features = false, path = '../runtime-modules/staking-handler'}
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
@@ -154,6 +155,7 @@ std = [
     'proposals-codex/std',
     'content-directory/std',
     'pallet_constitution/std',
+    'staking-handler/std',
 ]
 runtime-benchmarks = [
     "frame-system/runtime-benchmarks",

+ 8 - 4
runtime/src/integration/content_directory.rs

@@ -13,8 +13,10 @@ impl content_directory::ActorAuthenticator for Runtime {
         // get current lead id
         let maybe_current_lead_id = ContentDirectoryWorkingGroup::<Runtime>::current_lead();
         if let Some(ref current_lead_id) = maybe_current_lead_id {
-            if let Ok(worker) =
-                ContentDirectoryWorkingGroup::<Runtime>::ensure_worker_exists(current_lead_id)
+            if let Ok(worker) = working_group::ensure_worker_exists::<
+                Runtime,
+                ContentDirectoryWorkingGroupInstance,
+            >(current_lead_id)
             {
                 *account_id == worker.role_account_id
             } else {
@@ -26,8 +28,10 @@ impl content_directory::ActorAuthenticator for Runtime {
     }
 
     fn is_curator(curator_id: &Self::CuratorId, account_id: &AccountId) -> bool {
-        if let Ok(worker) =
-            ContentDirectoryWorkingGroup::<Runtime>::ensure_worker_exists(curator_id)
+        if let Ok(worker) = working_group::ensure_worker_exists::<
+            Runtime,
+            ContentDirectoryWorkingGroupInstance,
+        >(curator_id)
         {
             *account_id == worker.role_account_id
         } else {

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

@@ -1,6 +1,4 @@
 pub mod content_directory;
 pub mod proposals;
-pub mod staking_handler;
 pub mod storage;
 pub mod transactions;
-pub mod working_group;

+ 22 - 71
runtime/src/integration/proposals/proposal_encoder.rs

@@ -4,7 +4,6 @@ use proposals_codex::{ProposalDetails, ProposalDetailsOf, ProposalEncoder};
 use working_group::OpeningType;
 
 use codec::Encode;
-use frame_support::print;
 use sp_std::collections::btree_set::BTreeSet;
 use sp_std::marker::PhantomData;
 use sp_std::vec::Vec;
@@ -36,9 +35,6 @@ impl ProposalEncoder<Runtime> for ExtrinsicProposalEncoder {
             ProposalDetails::Text(text) => {
                 Call::ProposalsCodex(proposals_codex::Call::execute_text_proposal(text))
             }
-            ProposalDetails::SetElectionParameters(election_parameters) => Call::CouncilElection(
-                governance::election::Call::set_election_parameters(election_parameters),
-            ),
             ProposalDetails::Spending(balance, destination) => Call::Council(
                 governance::council::Call::spend_from_council_mint(balance, destination),
             ),
@@ -48,54 +44,20 @@ impl ProposalEncoder<Runtime> for ExtrinsicProposalEncoder {
             ProposalDetails::RuntimeUpgrade(wasm_code) => Call::ProposalsCodex(
                 proposals_codex::Call::execute_runtime_upgrade_proposal(wasm_code),
             ),
-            // ********** Deprecated during the Babylon release.
-            ProposalDetails::DeprecatedSetLead(_) => {
-                print("Error: Calling deprecated SetLead encoding option.");
-                return Vec::new();
-            }
-            // ********** Deprecated during the Babylon release.
-            ProposalDetails::DeprecatedSetContentWorkingGroupMintCapacity(_) => {
-                print(
-                    "Error: Calling deprecated SetContentWorkingGroupMintCapacity encoding option.",
-                );
-                return Vec::new();
-            }
-            // ********** Deprecated during the Nicaea release.
-            // It is kept only for backward compatibility in the Pioneer. **********
-            ProposalDetails::DeprecatedEvictStorageProvider(_) => {
-                print("Error: Calling deprecated EvictStorageProvider encoding option.");
-                return Vec::new();
-            }
-            // ********** Deprecated during the Nicaea release.
-            // It is kept only for backward compatibility in the Pioneer. **********
-            ProposalDetails::DeprecatedSetStorageRoleParameters(_) => {
-                print("Error: Calling deprecated SetStorageRoleParameters encoding option.");
-                return Vec::new();
-            }
             ProposalDetails::AddWorkingGroupLeaderOpening(add_opening_params) => {
                 wrap_working_group_call!(
                     add_opening_params.working_group,
                     Wg::create_add_opening_call(add_opening_params)
                 )
             }
-            ProposalDetails::BeginReviewWorkingGroupLeaderApplications(
-                opening_id,
-                working_group,
-            ) => wrap_working_group_call!(
-                working_group,
-                Wg::create_begin_review_applications_call(opening_id)
-            ),
             ProposalDetails::FillWorkingGroupLeaderOpening(fill_opening_params) => {
                 wrap_working_group_call!(
                     fill_opening_params.working_group,
                     Wg::create_fill_opening_call(fill_opening_params)
                 )
             }
-            ProposalDetails::SetWorkingGroupMintCapacity(mint_balance, working_group) => {
-                wrap_working_group_call!(
-                    working_group,
-                    Wg::create_set_mint_capacity_call(mint_balance)
-                )
+            ProposalDetails::SetWorkingGroupBudgetCapacity(budget, working_group) => {
+                wrap_working_group_call!(working_group, Wg::create_set_budget_capacity_call(budget))
             }
             ProposalDetails::DecreaseWorkingGroupLeaderStake(
                 worker_id,
@@ -144,7 +106,7 @@ struct Wg<T, I> {
 
 impl<T, I> Wg<T, I>
 where
-    T: working_group::Trait<I>,
+    T: working_group::Trait<I> + proposals_codex::Trait,
     I: frame_support::traits::Instance,
 {
     // Generic call constructor for the add working group opening.
@@ -155,28 +117,16 @@ where
         >,
     ) -> working_group::Call<T, I> {
         working_group::Call::<T, I>::add_opening(
-            add_opening_params.activate_at,
-            add_opening_params.commitment,
-            add_opening_params.human_readable_text,
+            add_opening_params.description,
             OpeningType::Leader,
+            add_opening_params.stake_policy,
+            add_opening_params.reward_policy,
         )
     }
 
-    // Generic call constructor for the begin review working group applications.
-    fn create_begin_review_applications_call(
-        opening_id: working_group::OpeningId<T>,
-    ) -> working_group::Call<T, I> {
-        working_group::Call::<T, I>::begin_applicant_review(opening_id)
-    }
-
     // Generic call constructor for the add working group opening.
     fn create_fill_opening_call(
-        fill_opening_params: proposals_codex::FillOpeningParameters<
-            T::BlockNumber,
-            minting::BalanceOf<T>,
-            working_group::OpeningId<T>,
-            working_group::ApplicationId<T>,
-        >,
+        fill_opening_params: proposals_codex::FillOpeningParameters,
     ) -> working_group::Call<T, I> {
         let mut successful_application_ids = BTreeSet::new();
         successful_application_ids.insert(fill_opening_params.successful_application_id);
@@ -184,17 +134,9 @@ where
         working_group::Call::<T, I>::fill_opening(
             fill_opening_params.opening_id,
             successful_application_ids,
-            fill_opening_params.reward_policy,
         )
     }
 
-    // Generic call constructor for the working group 'set mit capacity'.
-    fn create_set_mint_capacity_call(
-        mint_balance: working_group::BalanceOfMint<T>,
-    ) -> working_group::Call<T, I> {
-        working_group::Call::<T, I>::set_mint_capacity(mint_balance)
-    }
-
     // Generic call constructor for the working group 'decrease stake'.
     fn create_decrease_stake_call(
         worker_id: working_group::WorkerId<T>,
@@ -206,27 +148,36 @@ where
     // Generic call constructor for the working group 'slash stake'.
     fn create_slash_stake_call(
         worker_id: working_group::WorkerId<T>,
-        slashing_stake: working_group::BalanceOf<T>,
+        penalty: working_group::Penalty<working_group::BalanceOf<T>>,
     ) -> working_group::Call<T, I> {
-        working_group::Call::<T, I>::slash_stake(worker_id, slashing_stake)
+        working_group::Call::<T, I>::slash_stake(worker_id, penalty)
     }
 
     // Generic call constructor for the working group 'update reward amount'.
     fn create_set_reward_call(
         worker_id: working_group::WorkerId<T>,
-        reward_amount: working_group::BalanceOfMint<T>,
+        reward_amount: Option<working_group::BalanceOf<T>>,
     ) -> working_group::Call<T, I> {
         working_group::Call::<T, I>::update_reward_amount(worker_id, reward_amount)
     }
 
     // Generic call constructor for the working group 'terminate role'.
     fn terminate_role_call(
-        terminate_role_params: proposals_codex::TerminateRoleParameters<working_group::WorkerId<T>>,
+        terminate_role_params: proposals_codex::TerminateRoleParameters<
+            working_group::WorkerId<T>,
+            working_group::BalanceOf<T>,
+        >,
     ) -> working_group::Call<T, I> {
         working_group::Call::<T, I>::terminate_role(
             terminate_role_params.worker_id,
-            terminate_role_params.rationale,
-            terminate_role_params.slash,
+            terminate_role_params.penalty,
         )
     }
+
+    // Generic call constructor for the working group 'set budget'.
+    fn create_set_budget_capacity_call(
+        budget: working_group::BalanceOf<T>,
+    ) -> working_group::Call<T, I> {
+        working_group::Call::<T, I>::set_budget(budget)
+    }
 }

+ 0 - 93
runtime/src/integration/working_group.rs

@@ -1,93 +0,0 @@
-use frame_support::StorageMap;
-use sp_std::marker::PhantomData;
-
-use crate::{ContentDirectoryWorkingGroupInstance, StorageWorkingGroupInstance};
-use stake::{BalanceOf, NegativeImbalance};
-
-pub struct ContentDirectoryWGStakingEventsHandler<T> {
-    pub marker: PhantomData<T>,
-}
-
-impl<T: stake::Trait + working_group::Trait<ContentDirectoryWorkingGroupInstance>>
-    stake::StakingEventsHandler<T> for ContentDirectoryWGStakingEventsHandler<T>
-{
-    /// Unstake remaining sum back to the source_account_id
-    fn unstaked(
-        stake_id: &<T as stake::Trait>::StakeId,
-        _unstaked_amount: BalanceOf<T>,
-        remaining_imbalance: NegativeImbalance<T>,
-    ) -> NegativeImbalance<T> {
-        // Stake not related to a staked role managed by the hiring module.
-        if !hiring::ApplicationIdByStakingId::<T>::contains_key(*stake_id) {
-            return remaining_imbalance;
-        }
-
-        let hiring_application_id = hiring::ApplicationIdByStakingId::<T>::get(*stake_id);
-
-        if working_group::MemberIdByHiringApplicationId::<T, ContentDirectoryWorkingGroupInstance>::contains_key(
-            hiring_application_id,
-        ) {
-            return <working_group::Module<T, ContentDirectoryWorkingGroupInstance>>::refund_working_group_stake(
-				*stake_id,
-				remaining_imbalance,
-			);
-        }
-
-        remaining_imbalance
-    }
-
-    /// Empty handler for the slashing.
-    fn slashed(
-        _: &<T as stake::Trait>::StakeId,
-        _: Option<<T as stake::Trait>::SlashId>,
-        _: BalanceOf<T>,
-        _: BalanceOf<T>,
-        remaining_imbalance: NegativeImbalance<T>,
-    ) -> NegativeImbalance<T> {
-        remaining_imbalance
-    }
-}
-
-pub struct StorageWgStakingEventsHandler<T> {
-    pub marker: PhantomData<T>,
-}
-
-impl<T: stake::Trait + working_group::Trait<StorageWorkingGroupInstance>>
-    stake::StakingEventsHandler<T> for StorageWgStakingEventsHandler<T>
-{
-    /// Unstake remaining sum back to the source_account_id
-    fn unstaked(
-        stake_id: &<T as stake::Trait>::StakeId,
-        _unstaked_amount: BalanceOf<T>,
-        remaining_imbalance: NegativeImbalance<T>,
-    ) -> NegativeImbalance<T> {
-        // Stake not related to a staked role managed by the hiring module.
-        if !hiring::ApplicationIdByStakingId::<T>::contains_key(*stake_id) {
-            return remaining_imbalance;
-        }
-
-        let hiring_application_id = hiring::ApplicationIdByStakingId::<T>::get(*stake_id);
-
-        if working_group::MemberIdByHiringApplicationId::<T, StorageWorkingGroupInstance>::contains_key(
-            hiring_application_id,
-        ) {
-            return <working_group::Module<T, StorageWorkingGroupInstance>>::refund_working_group_stake(
-				*stake_id,
-				remaining_imbalance,
-			);
-        }
-
-        remaining_imbalance
-    }
-
-    /// Empty handler for the slashing.
-    fn slashed(
-        _: &<T as stake::Trait>::StakeId,
-        _: Option<<T as stake::Trait>::SlashId>,
-        _: BalanceOf<T>,
-        _: BalanceOf<T>,
-        remaining_imbalance: NegativeImbalance<T>,
-    ) -> NegativeImbalance<T> {
-        remaining_imbalance
-    }
-}

+ 34 - 12
runtime/src/lib.rs

@@ -473,10 +473,7 @@ parameter_types! {
 impl stake::Trait for Runtime {
     type Currency = <Self as common::currency::GovernanceCurrency>::Currency;
     type StakePoolId = StakePoolId;
-    type StakingEventsHandler = (
-        crate::integration::working_group::ContentDirectoryWGStakingEventsHandler<Self>,
-        crate::integration::working_group::StorageWgStakingEventsHandler<Self>,
-    );
+    type StakingEventsHandler = ();
     type StakeId = u64;
     type SlashId = u64;
 }
@@ -573,7 +570,11 @@ impl forum::Trait for Runtime {
         // get current lead id
         let maybe_current_lead_id = ForumGroup::<Runtime>::current_lead();
         if let Some(ref current_lead_id) = maybe_current_lead_id {
-            if let Ok(worker) = ForumGroup::<Runtime>::ensure_worker_exists(current_lead_id) {
+            if let Ok(worker) = working_group::ensure_worker_exists::<
+                Runtime,
+                ForumWorkingGroupInstance,
+            >(current_lead_id)
+            {
                 *_account_id == worker.role_account_id
             } else {
                 false
@@ -592,7 +593,9 @@ impl forum::Trait for Runtime {
     }
 
     fn is_moderator(_account_id: &Self::AccountId, _moderator_id: &Self::ModeratorId) -> bool {
-        if let Ok(worker) = ForumGroup::<Runtime>::ensure_worker_exists(_moderator_id) {
+        if let Ok(worker) =
+            working_group::ensure_worker_exists::<Runtime, ForumWorkingGroupInstance>(_moderator_id)
+        {
             *_account_id == worker.role_account_id
         } else {
             false
@@ -615,21 +618,40 @@ pub type ContentDirectoryWorkingGroupInstance = working_group::Instance3;
 
 parameter_types! {
     pub const MaxWorkerNumberLimit: u32 = 100;
+    pub const MinUnstakingPeriodLimit: u32 = 43200;
+    pub const ForumWorkingGroupRewardPeriod: u32 = 14400 + 10;
+    pub const StorageWorkingGroupRewardPeriod: u32 = 14400 + 20;
+    pub const ContentWorkingGroupRewardPeriod: u32 = 14400 + 30;
+    pub const StorageWorkingGroupLockId: LockIdentifier = [6; 8];
+    pub const ContentWorkingGroupLockId: LockIdentifier = [7; 8];
+    pub const ForumGroupLockId: LockIdentifier = [8; 8];
 }
 
 impl working_group::Trait<ForumWorkingGroupInstance> for Runtime {
     type Event = Event;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, ForumGroupLockId>;
+    type MemberOriginValidator = MembershipOriginValidator<Self>;
+    type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
+    type RewardPeriod = ForumWorkingGroupRewardPeriod;
 }
 
 impl working_group::Trait<StorageWorkingGroupInstance> for Runtime {
     type Event = Event;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, StorageWorkingGroupLockId>;
+    type MemberOriginValidator = MembershipOriginValidator<Self>;
+    type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
+    type RewardPeriod = StorageWorkingGroupRewardPeriod;
 }
 
 impl working_group::Trait<ContentDirectoryWorkingGroupInstance> for Runtime {
     type Event = Event;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, ContentWorkingGroupLockId>;
+    type MemberOriginValidator = MembershipOriginValidator<Self>;
+    type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
+    type RewardPeriod = ContentWorkingGroupRewardPeriod;
 }
 
 impl service_discovery::Trait for Runtime {
@@ -651,7 +673,7 @@ impl proposals_engine::Trait for Runtime {
     type VoterOriginValidator = CouncilManager<Self>;
     type TotalVotersCounter = CouncilManager<Self>;
     type ProposalId = u32;
-    type StakingHandler = integration::staking_handler::StakingManager<Self, ProposalsLockId>;
+    type StakingHandler = staking_handler::StakingManager<Self, ProposalsLockId>;
     type CancellationFee = ProposalCancellationFee;
     type RejectionFee = ProposalRejectionFee;
     type TitleMaxLength = ProposalTitleMaxLength;
@@ -700,8 +722,8 @@ impl proposals_codex::Trait for Runtime {
     type BeginReviewWorkingGroupApplicationsProposalParameters =
         BeginReviewWorkingGroupApplicationsProposalParameters;
     type FillWorkingGroupOpeningProposalParameters = FillWorkingGroupOpeningProposalParameters;
-    type SetWorkingGroupMintCapacityProposalParameters =
-        SetWorkingGroupMintCapacityProposalParameters;
+    type SetWorkingGroupBudgetCapacityProposalParameters =
+        SetWorkingGroupBudgetCapacityProposalParameters;
     type DecreaseWorkingGroupLeaderStakeProposalParameters =
         DecreaseWorkingGroupLeaderStakeProposalParameters;
     type SlashWorkingGroupLeaderStakeProposalParameters =
@@ -793,8 +815,8 @@ construct_runtime!(
         ProposalsDiscussion: proposals_discussion::{Module, Call, Storage, Event<T>},
         ProposalsCodex: proposals_codex::{Module, Call, Storage},
         // --- Working groups
-        ForumWorkingGroup: working_group::<Instance1>::{Module, Call, Storage, Config<T>, Event<T>},
-        StorageWorkingGroup: working_group::<Instance2>::{Module, Call, Storage, Config<T>, Event<T>},
-        ContentDirectoryWorkingGroup: working_group::<Instance3>::{Module, Call, Storage, Config<T>, Event<T>},
+        ForumWorkingGroup: working_group::<Instance1>::{Module, Call, Storage, Event<T>},
+        StorageWorkingGroup: working_group::<Instance2>::{Module, Call, Storage, Event<T>},
+        ContentDirectoryWorkingGroup: working_group::<Instance3>::{Module, Call, Storage, Event<T>},
     }
 );

+ 3 - 3
runtime/src/proposals_configuration/defaults.rs

@@ -100,9 +100,9 @@ pub(crate) fn fill_working_group_opening_proposal() -> ProposalParameters<BlockN
     }
 }
 
-// Proposal parameters for the 'Set working group mint capacity' proposal
-pub(crate) fn set_working_group_mint_capacity_proposal() -> ProposalParameters<BlockNumber, Balance>
-{
+// Proposal parameters for the 'Set working group budget capacity' proposal
+pub(crate) fn set_working_group_budget_capacity_proposal(
+) -> ProposalParameters<BlockNumber, Balance> {
     ProposalParameters {
         voting_period: 43200,
         grace_period: 0,

+ 5 - 5
runtime/src/proposals_configuration/mod.rs

@@ -29,7 +29,7 @@ parameter_types! {
     pub AddWorkingGroupOpeningProposalParameters: ProposalParameters<BlockNumber, Balance> = ALL_PROPOSALS_PARAMETERS.add_working_group_opening_proposal;
     pub BeginReviewWorkingGroupApplicationsProposalParameters: ProposalParameters<BlockNumber, Balance> = ALL_PROPOSALS_PARAMETERS.begin_review_working_group_applications_proposal;
     pub FillWorkingGroupOpeningProposalParameters: ProposalParameters<BlockNumber, Balance> = ALL_PROPOSALS_PARAMETERS.fill_working_group_opening_proposal;
-    pub SetWorkingGroupMintCapacityProposalParameters: ProposalParameters<BlockNumber, Balance> = ALL_PROPOSALS_PARAMETERS.set_working_group_mint_capacity_proposal;
+    pub SetWorkingGroupBudgetCapacityProposalParameters: ProposalParameters<BlockNumber, Balance> = ALL_PROPOSALS_PARAMETERS.set_working_group_budget_capacity_proposal;
     pub DecreaseWorkingGroupLeaderStakeProposalParameters: ProposalParameters<BlockNumber, Balance> = ALL_PROPOSALS_PARAMETERS.decrease_working_group_leader_stake_proposal;
     pub SlashWorkingGroupLeaderStakeProposalParameters: ProposalParameters<BlockNumber, Balance> = ALL_PROPOSALS_PARAMETERS.slash_working_group_leader_stake_proposal;
     pub SetWorkingGroupLeaderRewardProposalParameters: ProposalParameters<BlockNumber, Balance> = ALL_PROPOSALS_PARAMETERS.set_working_group_leader_reward_proposal;
@@ -47,7 +47,7 @@ struct AllProposalsParameters {
     pub add_working_group_opening_proposal: ProposalParameters<BlockNumber, Balance>,
     pub begin_review_working_group_applications_proposal: ProposalParameters<BlockNumber, Balance>,
     pub fill_working_group_opening_proposal: ProposalParameters<BlockNumber, Balance>,
-    pub set_working_group_mint_capacity_proposal: ProposalParameters<BlockNumber, Balance>,
+    pub set_working_group_budget_capacity_proposal: ProposalParameters<BlockNumber, Balance>,
     pub decrease_working_group_leader_stake_proposal: ProposalParameters<BlockNumber, Balance>,
     pub slash_working_group_leader_stake_proposal: ProposalParameters<BlockNumber, Balance>,
     pub set_working_group_leader_reward_proposal: ProposalParameters<BlockNumber, Balance>,
@@ -119,7 +119,7 @@ fn convert_json_object_to_proposal_parameters(
         init_proposal_parameter_object!(
             params,
             jo.clone(),
-            set_working_group_mint_capacity_proposal
+            set_working_group_budget_capacity_proposal
         );
         init_proposal_parameter_object!(
             params,
@@ -254,8 +254,8 @@ fn default_parameters() -> AllProposalsParameters {
         begin_review_working_group_applications_proposal:
             defaults::begin_review_working_group_applications_proposal(),
         fill_working_group_opening_proposal: defaults::fill_working_group_opening_proposal(),
-        set_working_group_mint_capacity_proposal:
-            defaults::set_working_group_mint_capacity_proposal(),
+        set_working_group_budget_capacity_proposal:
+            defaults::set_working_group_budget_capacity_proposal(),
         decrease_working_group_leader_stake_proposal:
             defaults::decrease_working_group_leader_stake_proposal(),
         slash_working_group_leader_stake_proposal:

+ 1 - 1
runtime/src/proposals_configuration/sample_proposal_parameters.json

@@ -16,7 +16,7 @@
   "add_working_group_opening_proposal": {},
   "begin_review_working_group_applications_proposal": {},
   "fill_working_group_opening_proposal": {},
-  "set_working_group_mint_capacity_proposal": {},
+  "set_working_group_budget_capacity_proposal": {},
   "decrease_working_group_leader_stake_proposal": {},
   "slash_working_group_leader_stake_proposal": {},
   "set_working_group_leader_reward_proposal": {},

+ 20 - 26
runtime/src/runtime_api.rs

@@ -1,5 +1,5 @@
 use frame_support::inherent::{CheckInherentsResult, InherentData};
-use frame_support::traits::{KeyOwnerProofSystem, OnRuntimeUpgrade, Randomness};
+use frame_support::traits::{KeyOwnerProofSystem, Randomness};
 use frame_support::unsigned::{TransactionSource, TransactionValidity};
 use pallet_grandpa::fg_primitives;
 use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
@@ -11,7 +11,6 @@ use sp_runtime::{generic, ApplyExtrinsicResult};
 use sp_std::vec::Vec;
 
 use crate::constants::PRIMARY_PROBABILITY;
-use crate::integration::content_directory::ContentDirectoryWorkingGroup;
 use crate::{
     AccountId, AuthorityDiscoveryId, Balance, BlockNumber, EpochDuration, GrandpaAuthorityList,
     GrandpaId, Hash, Index, RuntimeVersion, Signature, VERSION,
@@ -20,7 +19,6 @@ use crate::{
     AllModules, AuthorityDiscovery, Babe, Call, Grandpa, Historical, InherentDataExt,
     RandomnessCollectiveFlip, Runtime, SessionKeys, System, TransactionPayment,
 };
-use frame_support::weights::Weight;
 
 /// The SignedExtension to the basic transaction logic.
 pub type SignedExtra = (
@@ -54,37 +52,33 @@ pub type BlockId = generic::BlockId<Block>;
 pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<AccountId, Call, Signature, SignedExtra>;
 
 // Default Executive type without the RuntimeUpgrade
-// pub type Executive =
-//     frame_executive::Executive<Runtime, Block, frame_system::ChainContext<Runtime>, Runtime, AllModules>;
-
-/// Custom runtime upgrade handler.
-pub struct CustomOnRuntimeUpgrade;
-impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade {
-    fn on_runtime_upgrade() -> Weight {
-        let default_text_constraint = crate::working_group::default_text_constraint();
-        let default_content_working_group_mint_capacity = 0;
-
-        ContentDirectoryWorkingGroup::<Runtime>::initialize_working_group(
-            default_text_constraint,
-            default_text_constraint,
-            default_text_constraint,
-            default_content_working_group_mint_capacity,
-        );
-
-        10_000_000 // TODO: adjust weight
-    }
-}
-
-/// Executive: handles dispatch to the various modules.
 pub type Executive = frame_executive::Executive<
     Runtime,
     Block,
     frame_system::ChainContext<Runtime>,
     Runtime,
     AllModules,
-    CustomOnRuntimeUpgrade,
 >;
 
+// /// Custom runtime upgrade handler.
+// pub struct CustomOnRuntimeUpgrade;
+// impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade {
+//     fn on_runtime_upgrade() -> Weight {
+//
+//         10_000_000 // TODO: adjust weight
+//     }
+// }
+//
+// /// Executive: handles dispatch to the various modules with CustomOnRuntimeUpgrade.
+// pub type Executive = frame_executive::Executive<
+//     Runtime,
+//     Block,
+//     frame_system::ChainContext<Runtime>,
+//     Runtime,
+//     AllModules,
+//     CustomOnRuntimeUpgrade,
+// >;
+
 /// Export of the private const generated within the macro.
 pub const EXPORTED_RUNTIME_API_VERSIONS: sp_version::ApisVec = RUNTIME_API_VERSIONS;
 

+ 108 - 370
runtime/src/tests/proposals_integration/working_group_proposals.rs

@@ -6,17 +6,14 @@ use super::*;
 use frame_system::RawOrigin;
 
 use common::working_group::WorkingGroup;
-use hiring::ActivateOpeningAt;
 use proposals_codex::AddOpeningParameters;
-use working_group::{OpeningPolicyCommitment, RewardPolicy};
+use working_group::Penalty;
 
+use crate::primitives::{ActorId, MemberId};
 use crate::{
     Balance, BlockNumber, ContentDirectoryWorkingGroup, ContentDirectoryWorkingGroupInstance,
     ForumWorkingGroup, ForumWorkingGroupInstance, StorageWorkingGroup, StorageWorkingGroupInstance,
 };
-use sp_std::collections::btree_set::BTreeSet;
-
-use crate::primitives::{ActorId, MemberId};
 use frame_support::traits;
 use strum::IntoEnumIterator;
 
@@ -27,8 +24,7 @@ type Hiring = hiring::Module<Runtime>;
 fn add_opening(
     member_id: MemberId,
     account_id: [u8; 32],
-    activate_at: hiring::ActivateOpeningAt<BlockNumber>,
-    opening_policy_commitment: Option<OpeningPolicyCommitment<BlockNumber, u128>>,
+    stake_policy: Option<working_group::StakePolicy<BlockNumber, Balance>>,
     sequence_number: u32, // action sequence number to align with other actions
     working_group: WorkingGroup,
 ) -> u64 {
@@ -70,11 +66,9 @@ fn add_opening(
             b"body".to_vec(),
             Some(account_id.into()),
             AddOpeningParameters {
-                activate_at: activate_at.clone(),
-                commitment: opening_policy_commitment
-                    .clone()
-                    .unwrap_or(OpeningPolicyCommitment::default()),
-                human_readable_text: Vec::new(),
+                description: Vec::new(),
+                stake_policy: stake_policy.clone(),
+                reward_policy: None,
                 working_group,
             },
             None,
@@ -88,41 +82,11 @@ fn add_opening(
     opening_id
 }
 
-fn begin_review_applications(
-    member_id: MemberId,
-    account_id: [u8; 32],
-    opening_id: u64,
-    sequence_number: u32, // action sequence number to align with other actions
-    working_group: WorkingGroup,
-) {
-    let expected_proposal_id = sequence_number;
-    let run_to_block = sequence_number * 2;
-
-    let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_begin_review_working_group_leader_applications_proposal(
-            RawOrigin::Signed(account_id.into()).into(),
-            member_id,
-            b"title".to_vec(),
-            b"body".to_vec(),
-            Some(account_id.into()),
-            opening_id,
-            working_group,
-            None,
-        )
-    })
-    .disable_setup_enviroment()
-    .with_expected_proposal_id(expected_proposal_id)
-    .with_run_to_block(run_to_block);
-
-    codex_extrinsic_test_fixture.call_extrinsic_and_assert();
-}
-
 fn fill_opening(
     member_id: MemberId,
     account_id: [u8; 32],
     opening_id: u64,
     successful_application_id: u64,
-    reward_policy: Option<RewardPolicy<Balance, BlockNumber>>,
     sequence_number: u32, // action sequence number to align with other actions
     working_group: WorkingGroup,
 ) {
@@ -139,7 +103,6 @@ fn fill_opening(
             proposals_codex::FillOpeningParameters {
                 opening_id,
                 successful_application_id,
-                reward_policy: reward_policy.clone(),
                 working_group,
             },
             None,
@@ -210,7 +173,10 @@ fn slash_stake(
             b"body".to_vec(),
             Some(account_id.into()),
             leader_worker_id,
-            stake_amount,
+            Penalty {
+                slashing_amount: stake_amount,
+                slashing_text: Vec::new(),
+            },
             working_group,
             None,
         )
@@ -241,7 +207,7 @@ fn set_reward(
             b"body".to_vec(),
             Some(account_id.into()),
             leader_worker_id,
-            reward_amount,
+            Some(reward_amount),
             working_group,
             None,
         )
@@ -254,7 +220,7 @@ fn set_reward(
 }
 
 fn set_mint_capacity<
-    T: working_group::Trait<I> + frame_system::Trait + minting::Trait,
+    T: working_group::Trait<I> + frame_system::Trait,
     I: frame_support::traits::Instance,
 >(
     member_id: MemberId,
@@ -263,21 +229,12 @@ fn set_mint_capacity<
     sequence_number: u32, // action sequence number to align with other actions
     setup_environment: bool,
     working_group: WorkingGroup,
-) where
-    <T as minting::Trait>::MintId: From<u64>,
-{
+) {
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
-    let mint_id_result = <minting::Module<Runtime>>::add_mint(0, None);
-
-    if let Ok(mint_id) = mint_id_result {
-        let mint_id: <T as minting::Trait>::MintId = mint_id.into();
-        <working_group::Mint<T, I>>::put(mint_id);
-    }
-
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
-        ProposalCodex::create_set_working_group_mint_capacity_proposal(
+        ProposalCodex::create_set_working_group_budget_capacity_proposal(
             RawOrigin::Signed(account_id.into()).into(),
             member_id,
             b"title".to_vec(),
@@ -299,7 +256,6 @@ fn terminate_role(
     member_id: MemberId,
     account_id: [u8; 32],
     leader_worker_id: u64,
-    slash: bool,
     sequence_number: u32, // action sequence number to align with other actions
     working_group: WorkingGroup,
 ) {
@@ -315,8 +271,10 @@ fn terminate_role(
             Some(account_id.into()),
             proposals_codex::TerminateRoleParameters {
                 worker_id: leader_worker_id,
-                rationale: Vec::new(),
-                slash,
+                penalty: Some(Penalty {
+                    slashing_amount: 100,
+                    slashing_text: Vec::new(),
+                }),
                 working_group,
             },
             None,
@@ -363,7 +321,6 @@ fn run_create_add_working_group_leader_opening_proposal_execution_succeeds<
     working_group: WorkingGroup,
 ) where
     <T as membership::Trait>::MemberId: From<u64>,
-    <T as hiring::Trait>::OpeningId: From<u64>,
 {
     initial_test_ext().execute_with(|| {
         let member_id: MemberId = 1;
@@ -375,15 +332,8 @@ fn run_create_add_working_group_leader_opening_proposal_execution_succeeds<
             next_opening_id
         ));
 
-        let opening_id: <T as hiring::Trait>::OpeningId = add_opening(
-            member_id,
-            account_id,
-            ActivateOpeningAt::CurrentBlock,
-            None,
-            1,
-            working_group,
-        )
-        .into();
+        let opening_id: working_group::OpeningId =
+            add_opening(member_id, account_id, None, 1, working_group).into();
 
         // Check for expected opening id.
         assert_eq!(opening_id, next_opening_id);
@@ -393,94 +343,6 @@ fn run_create_add_working_group_leader_opening_proposal_execution_succeeds<
     });
 }
 
-#[test]
-fn create_begin_review_working_group_leader_applications_proposal_execution_succeeds() {
-    // This uses strum crate for enum iteration
-    for group in WorkingGroup::iter() {
-        match group {
-            WorkingGroup::Content => {
-                run_create_begin_review_working_group_leader_applications_proposal_execution_succeeds::<
-                Runtime,
-                ContentDirectoryWorkingGroupInstance,
-            >(group);
-            }
-            WorkingGroup::Storage => {
-                run_create_begin_review_working_group_leader_applications_proposal_execution_succeeds::<
-                Runtime,
-                StorageWorkingGroupInstance,
-            >(group);
-            }
-            WorkingGroup::Forum => {
-                run_create_begin_review_working_group_leader_applications_proposal_execution_succeeds::<
-                Runtime,
-                ForumWorkingGroupInstance,
-            >(group);
-            }
-        }
-    }
-}
-
-fn run_create_begin_review_working_group_leader_applications_proposal_execution_succeeds<
-    T: working_group::Trait<I> + frame_system::Trait + stake::Trait,
-    I: frame_support::traits::Instance,
->(
-    working_group: WorkingGroup,
-) where
-    <T as hiring::Trait>::OpeningId: From<u64> + Into<u64>,
-{
-    initial_test_ext().execute_with(|| {
-        let member_id: MemberId = 1;
-        let account_id: [u8; 32] = [member_id as u8; 32];
-
-        let opening_id = add_opening(
-            member_id,
-            account_id,
-            ActivateOpeningAt::CurrentBlock,
-            None,
-            1,
-            working_group,
-        );
-
-        let opening = WorkingGroupInstance::<T, I>::opening_by_id(
-            <T as hiring::Trait>::OpeningId::from(opening_id),
-        );
-
-        let hiring_opening_id: u64 = opening.hiring_opening_id.into();
-        let hiring_opening = Hiring::opening_by_id(hiring_opening_id);
-        assert_eq!(
-            hiring_opening.stage,
-            hiring::OpeningStage::Active {
-                stage: hiring::ActiveOpeningStage::AcceptingApplications {
-                    started_accepting_applicants_at_block: 1
-                },
-                applications_added: BTreeSet::new(),
-                active_application_count: 0,
-                unstaking_application_count: 0,
-                deactivated_application_count: 0
-            }
-        );
-
-        begin_review_applications(member_id, account_id, opening_id, 2, working_group);
-        let grace_period = 14400;
-        run_to_block(grace_period + 10);
-
-        let hiring_opening = Hiring::opening_by_id(hiring_opening_id);
-        assert_eq!(
-            hiring_opening.stage,
-            hiring::OpeningStage::Active {
-                stage: hiring::ActiveOpeningStage::ReviewPeriod {
-                    started_accepting_applicants_at_block: 1,
-                    started_review_period_at_block: grace_period + 3,
-                },
-                applications_added: BTreeSet::new(),
-                active_application_count: 0,
-                unstaking_application_count: 0,
-                deactivated_application_count: 0
-            }
-        );
-    });
-}
-
 #[test]
 fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
     // This uses strum crate for enum iteration
@@ -515,54 +377,43 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
     ) where
         <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
         <T as membership::Trait>::MemberId: From<u64>,
-        <T as hiring::Trait>::OpeningId: From<u64>,
+        working_group::MemberId<T>: From<u64>,
     {
         initial_test_ext().execute_with(|| {
-            let member_id: MemberId = 1;
+            let member_id: u64 = 1;
             let account_id: [u8; 32] = [member_id as u8; 32];
 
-            let opening_id = add_opening(
-                member_id,
-                account_id,
-                ActivateOpeningAt::CurrentBlock,
-                None,
-                1,
-                working_group,
-            );
+            let opening_id = add_opening(member_id, account_id, None, 1, working_group);
 
             let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
                 RawOrigin::Signed(account_id.into()).into(),
-                member_id.into(),
-                opening_id.into(),
-                account_id.into(),
-                None,
-                None,
-                Vec::new(),
+                working_group::ApplyOnOpeningParameters::<T> {
+                    member_id: member_id.into(),
+                    opening_id,
+                    role_account_id: account_id.into(),
+                    reward_account_id: account_id.into(),
+                    description: Vec::new(),
+                    stake_parameters: None,
+                },
             );
 
             assert_eq!(apply_result, Ok(()));
 
             let expected_application_id = 0;
 
-            begin_review_applications(member_id, account_id, opening_id, 2, working_group);
-
             let lead = WorkingGroupInstance::<T, I>::current_lead();
             assert!(lead.is_none());
 
-            let grace_period_for_begin_application_proposal = 14400;
-            run_to_block(grace_period_for_begin_application_proposal + 20);
-
             fill_opening(
                 member_id,
                 account_id,
                 opening_id,
                 expected_application_id,
-                None,
-                3,
+                2,
                 working_group,
             );
 
-            run_to_block(grace_period_for_begin_application_proposal + 30);
+            run_to_block(30);
 
             let lead = WorkingGroupInstance::<T, I>::current_lead();
             assert!(lead.is_some());
@@ -603,7 +454,6 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
         working_group: WorkingGroup,
     ) where
         <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-        <T as hiring::Trait>::OpeningId: From<u64>,
         <T as membership::Trait>::MemberId: From<u64>,
         <T as membership::Trait>::ActorId: Into<u64>,
         <<T as stake::Trait>::Currency as traits::Currency<
@@ -615,41 +465,29 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
             let account_id: [u8; 32] = [member_id as u8; 32];
             let stake_amount: Balance = 100;
 
-            let opening_policy_commitment = OpeningPolicyCommitment {
-                role_staking_policy: Some(hiring::StakingPolicy {
-                    amount: 100,
-                    amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                    crowded_out_unstaking_period_length: None,
-                    review_period_expired_unstaking_period_length: None,
-                }),
-                ..OpeningPolicyCommitment::default()
-            };
+            let stake_policy = Some(working_group::StakePolicy {
+                stake_amount: 100,
+                leaving_unstaking_period: 0,
+            });
 
-            let opening_id = add_opening(
-                member_id,
-                account_id,
-                ActivateOpeningAt::CurrentBlock,
-                Some(opening_policy_commitment),
-                1,
-                working_group,
-            );
+            let opening_id = add_opening(member_id, account_id, stake_policy, 1, working_group);
 
             let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
                 RawOrigin::Signed(account_id.into()).into(),
-                member_id.into(),
-                opening_id.into(),
-                account_id.into(),
-                Some(stake_amount.into()),
-                None,
-                Vec::new(),
+                working_group::ApplyOnOpeningParameters::<T> {
+                    member_id: member_id.into(),
+                    opening_id,
+                    role_account_id: account_id.into(),
+                    reward_account_id: account_id.into(),
+                    description: Vec::new(),
+                    stake_parameters: None,
+                },
             );
 
             assert_eq!(apply_result, Ok(()));
 
             let expected_application_id = 0;
 
-            begin_review_applications(member_id, account_id, opening_id, 2, working_group);
-
             let lead = WorkingGroupInstance::<T, I>::current_lead();
             assert!(lead.is_none());
 
@@ -658,7 +496,6 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                 account_id,
                 opening_id,
                 expected_application_id,
-                None,
                 3,
                 working_group,
             );
@@ -726,7 +563,6 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
         working_group: WorkingGroup,
     ) where
         <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
-        <T as hiring::Trait>::OpeningId: From<u64>,
         <T as membership::Trait>::MemberId: From<u64>,
         <T as membership::Trait>::ActorId: Into<u64>,
         <<T as stake::Trait>::Currency as traits::Currency<
@@ -738,41 +574,29 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
             let account_id: [u8; 32] = [member_id as u8; 32];
             let stake_amount: Balance = 100;
 
-            let opening_policy_commitment = OpeningPolicyCommitment {
-                role_staking_policy: Some(hiring::StakingPolicy {
-                    amount: 100,
-                    amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                    crowded_out_unstaking_period_length: None,
-                    review_period_expired_unstaking_period_length: None,
-                }),
-                ..OpeningPolicyCommitment::default()
-            };
+            let stake_policy = Some(working_group::StakePolicy {
+                stake_amount: 100,
+                leaving_unstaking_period: 0,
+            });
 
-            let opening_id = add_opening(
-                member_id,
-                account_id,
-                ActivateOpeningAt::CurrentBlock,
-                Some(opening_policy_commitment),
-                1,
-                working_group,
-            );
+            let opening_id = add_opening(member_id, account_id, stake_policy, 1, working_group);
 
             let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
                 RawOrigin::Signed(account_id.into()).into(),
-                member_id.into(),
-                opening_id.into(),
-                account_id.into(),
-                Some(stake_amount.into()),
-                None,
-                Vec::new(),
+                working_group::ApplyOnOpeningParameters::<T> {
+                    member_id: member_id.into(),
+                    opening_id,
+                    role_account_id: account_id.into(),
+                    reward_account_id: account_id.into(),
+                    description: Vec::new(),
+                    stake_parameters: None,
+                },
             );
 
             assert_eq!(apply_result, Ok(()));
 
             let expected_application_id = 0;
 
-            begin_review_applications(member_id, account_id, opening_id, 2, working_group);
-
             let lead = WorkingGroupInstance::<T, I>::current_lead();
 
             assert!(lead.is_none());
@@ -782,7 +606,6 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                 account_id,
                 opening_id,
                 expected_application_id,
-                None,
                 3,
                 working_group,
             );
@@ -851,16 +674,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
             <T as frame_system::Trait>::AccountId: From<[u8; 32]>,
             <T as membership::Trait>::MemberId: From<u64>,
             <T as minting::Trait>::MintId: From<u64>,
-            <<T as minting::Trait>::Currency as traits::Currency<
-                <T as frame_system::Trait>::AccountId,
-            >>::Balance: From<u128>,
+            working_group::BalanceOf<T>: From<u128>,
         {
             initial_test_ext().execute_with(|| {
                 let member_id: MemberId = 1;
                 let account_id: [u8; 32] = [member_id as u8; 32];
 
-                assert_eq!(WorkingGroupInstance::<T, I>::mint(), 0.into());
-
                 let mint_capacity = 999999;
                 set_mint_capacity::<T, I>(
                     member_id,
@@ -871,10 +690,10 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     working_group,
                 );
 
-                let mint_id = WorkingGroupInstance::<T, I>::mint();
-                let mint = <minting::Module<T>>::mints(mint_id);
-
-                assert_eq!(mint.capacity(), mint_capacity.into());
+                assert_eq!(
+                    working_group::Module::<T, I>::budget(),
+                    mint_capacity.into()
+                );
             });
         }
 
@@ -915,61 +734,38 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
             <T as membership::Trait>::MemberId: From<u64>,
             <T as membership::Trait>::ActorId: Into<u64>,
             <T as minting::Trait>::MintId: From<u64>,
-            <T as hiring::Trait>::OpeningId: From<u64>,
-            <<T as minting::Trait>::Currency as traits::Currency<
-                <T as frame_system::Trait>::AccountId,
-            >>::Balance: From<u128>,
+            working_group::BalanceOf<T>: From<u128>,
         {
             initial_test_ext().execute_with(|| {
                 let member_id: MemberId = 1;
                 let account_id: [u8; 32] = [member_id as u8; 32];
-                let stake_amount = 100;
-
-                let opening_policy_commitment = OpeningPolicyCommitment {
-                    role_staking_policy: Some(hiring::StakingPolicy {
-                        amount: 100,
-                        amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                        crowded_out_unstaking_period_length: None,
-                        review_period_expired_unstaking_period_length: None,
-                    }),
-                    ..OpeningPolicyCommitment::default()
-                };
 
-                let opening_id = add_opening(
-                    member_id,
-                    account_id,
-                    ActivateOpeningAt::CurrentBlock,
-                    Some(opening_policy_commitment),
-                    1,
-                    working_group,
-                );
+                let stake_policy = Some(working_group::StakePolicy {
+                    stake_amount: 100,
+                    leaving_unstaking_period: 0,
+                });
+
+                let opening_id = add_opening(member_id, account_id, stake_policy, 1, working_group);
 
                 let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
                     RawOrigin::Signed(account_id.into()).into(),
-                    member_id.into(),
-                    opening_id.into(),
-                    account_id.into(),
-                    Some(stake_amount.into()),
-                    None,
-                    Vec::new(),
+                    working_group::ApplyOnOpeningParameters::<T> {
+                        member_id: member_id.into(),
+                        opening_id,
+                        role_account_id: account_id.into(),
+                        reward_account_id: account_id.into(),
+                        description: Vec::new(),
+                        stake_parameters: None,
+                    },
                 );
 
                 assert_eq!(apply_result, Ok(()));
 
                 let expected_application_id = 0;
 
-                begin_review_applications(member_id, account_id, opening_id, 2, working_group);
-
                 let lead = WorkingGroupInstance::<T, I>::current_lead();
                 assert!(lead.is_none());
 
-                let old_reward_amount = 100;
-                let reward_policy = Some(RewardPolicy {
-                    amount_per_payout: old_reward_amount,
-                    next_payment_at_block: 9999,
-                    payout_interval: None,
-                });
-
                 set_mint_capacity::<T, I>(member_id, account_id, 999999, 3, false, working_group);
 
                 fill_opening(
@@ -977,20 +773,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     account_id,
                     opening_id,
                     expected_application_id,
-                    reward_policy,
                     4,
                     working_group,
                 );
 
                 let leader_worker_id = WorkingGroupInstance::<T, I>::current_lead().unwrap();
 
-                let worker = WorkingGroupInstance::<T, I>::worker_by_id(leader_worker_id);
-                let relationship_id = worker.reward_relationship.unwrap();
-
-                let relationship =
-                    recurring_rewards::RewardRelationships::<T>::get(relationship_id);
-                assert_eq!(relationship.amount_per_payout, old_reward_amount.into());
-
                 let new_reward_amount = 999;
                 set_reward(
                     member_id,
@@ -1001,9 +789,9 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     working_group,
                 );
 
-                let relationship =
-                    recurring_rewards::RewardRelationships::<T>::get(relationship_id);
-                assert_eq!(relationship.amount_per_payout, new_reward_amount.into());
+                let worker = WorkingGroupInstance::<T, I>::worker_by_id(leader_worker_id);
+
+                assert_eq!(worker.reward_per_block, Some(new_reward_amount.into()));
             });
         }
 
@@ -1042,63 +830,41 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
         ) 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 minting::Trait>::MintId: From<u64>,
-            <T as hiring::Trait>::OpeningId: From<u64>,
-            <<T as stake::Trait>::Currency as traits::Currency<
-                <T as frame_system::Trait>::AccountId,
-            >>::Balance: From<u128>,
         {
             initial_test_ext().execute_with(|| {
                 let member_id: MemberId = 1;
                 let account_id: [u8; 32] = [0; 32];
                 let stake_amount = 100_u128;
 
-                let opening_policy_commitment = OpeningPolicyCommitment {
-                    role_staking_policy: Some(hiring::StakingPolicy {
-                        amount: 100,
-                        amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                        crowded_out_unstaking_period_length: None,
-                        review_period_expired_unstaking_period_length: None,
-                    }),
-                    ..OpeningPolicyCommitment::default()
-                };
+                let stake_policy = Some(working_group::StakePolicy {
+                    stake_amount: 100,
+                    leaving_unstaking_period: 0,
+                });
 
-                let opening_id = add_opening(
-                    member_id.into(),
-                    account_id,
-                    ActivateOpeningAt::CurrentBlock,
-                    Some(opening_policy_commitment),
-                    1,
-                    working_group,
-                );
+                let opening_id = add_opening(member_id, account_id, stake_policy, 1, working_group);
 
                 let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
                     RawOrigin::Signed(account_id.into()).into(),
-                    member_id.into(),
-                    opening_id.into(),
-                    account_id.into(),
-                    Some(stake_amount.into()),
-                    None,
-                    Vec::new(),
+                    working_group::ApplyOnOpeningParameters::<T> {
+                        member_id: member_id.into(),
+                        opening_id,
+                        role_account_id: account_id.into(),
+                        reward_account_id: account_id.into(),
+                        description: Vec::new(),
+                        stake_parameters: None,
+                    },
                 );
 
                 assert_eq!(apply_result, Ok(()));
 
                 let expected_application_id = 0;
 
-                begin_review_applications(member_id, account_id, opening_id, 2, working_group);
-
                 let lead = WorkingGroupInstance::<T, I>::current_lead();
                 assert!(lead.is_none());
 
-                let old_reward_amount = 100;
-                let reward_policy = Some(RewardPolicy {
-                    amount_per_payout: old_reward_amount,
-                    next_payment_at_block: 9999,
-                    payout_interval: None,
-                });
-
                 set_mint_capacity::<T, I>(member_id, account_id, 999999, 3, false, working_group);
 
                 fill_opening(
@@ -1106,7 +872,6 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     account_id,
                     opening_id,
                     expected_application_id,
-                    reward_policy,
                     4,
                     working_group,
                 );
@@ -1123,7 +888,6 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     member_id,
                     account_id,
                     leader_worker_id.into(),
-                    false,
                     5,
                     working_group,
                 );
@@ -1165,68 +929,44 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
             <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 minting::Trait>::MintId: From<u64>,
-            <T as hiring::Trait>::OpeningId: From<u64>,
-            <<T as stake::Trait>::Currency as traits::Currency<
-                <T as frame_system::Trait>::AccountId,
-            >>::Balance: From<u128>,
         {
             initial_test_ext().execute_with(|| {
                 let member_id: MemberId = 1;
                 let account_id: [u8; 32] = [0; 32];
                 let stake_amount = 100_u128;
 
-                let opening_policy_commitment = OpeningPolicyCommitment {
-                    role_staking_policy: Some(hiring::StakingPolicy {
-                        amount: 100,
-                        amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                        crowded_out_unstaking_period_length: None,
-                        review_period_expired_unstaking_period_length: None,
-                    }),
-                    ..OpeningPolicyCommitment::default()
+                let staking_policy = working_group::StakePolicy {
+                    stake_amount: 100,
+                    leaving_unstaking_period: 0,
                 };
 
                 let opening_id = add_opening(
                     member_id,
                     account_id,
-                    ActivateOpeningAt::CurrentBlock,
-                    Some(opening_policy_commitment),
+                    Some(staking_policy),
                     1,
                     working_group,
                 );
 
                 let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
                     RawOrigin::Signed(account_id.into()).into(),
-                    member_id.into(),
-                    opening_id.into(),
-                    account_id.into(),
-                    Some(stake_amount.into()),
-                    None,
-                    Vec::new(),
+                    working_group::ApplyOnOpeningParameters::<T> {
+                        member_id: member_id.into(),
+                        opening_id,
+                        role_account_id: account_id.into(),
+                        reward_account_id: account_id.into(),
+                        description: Vec::new(),
+                        stake_parameters: None,
+                    },
                 );
 
                 assert_eq!(apply_result, Ok(()));
 
                 let expected_application_id = 0;
 
-                begin_review_applications(
-                    member_id,
-                    account_id,
-                    opening_id.into(),
-                    2,
-                    working_group,
-                );
-
                 let lead = WorkingGroupInstance::<T, I>::current_lead();
                 assert!(lead.is_none());
 
-                let old_reward_amount = 100;
-                let reward_policy = Some(RewardPolicy {
-                    amount_per_payout: old_reward_amount,
-                    next_payment_at_block: 9999,
-                    payout_interval: None,
-                });
-
                 set_mint_capacity::<T, I>(member_id, account_id, 999999, 3, false, working_group);
 
                 fill_opening(
@@ -1234,7 +974,6 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     account_id,
                     opening_id,
                     expected_application_id,
-                    reward_policy,
                     4,
                     working_group,
                 );
@@ -1251,7 +990,6 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     member_id,
                     account_id,
                     leader_worker_id.into(),
-                    true,
                     5,
                     working_group,
                 );

+ 3 - 3
runtime/src/tests/storage_integration.rs

@@ -20,9 +20,9 @@ fn storage_provider_helper_succeeds() {
 		let worker_id2 = 7;
 		let worker_id3 = 19;
 
-		<working_group::WorkerById<Runtime, Instance2>>::insert(worker_id1, Worker::default());
-		<working_group::WorkerById<Runtime, Instance2>>::insert(worker_id2, Worker::default());
-		<working_group::WorkerById<Runtime, Instance2>>::insert(worker_id3, Worker::default());
+		<working_group::WorkerById<Runtime, Instance2>>::insert(worker_id1, Worker::<Runtime>::default());
+		<working_group::WorkerById<Runtime, Instance2>>::insert(worker_id2, Worker::<Runtime>::default());
+		<working_group::WorkerById<Runtime, Instance2>>::insert(worker_id3, Worker::<Runtime>::default());
 
 		// Still error - not registered in the service discovery.
 		let random_provider_result = <StorageProviderHelper as storage::data_directory::StorageProviderHelper<Runtime>>::get_random_storage_provider();

Some files were not shown because too many files changed in this diff