瀏覽代碼

Merge pull request #728 from shamil-gadelshin/working_group_hirable_lead

Changes to the working group: make leader hireable.
Bedeho Mender 4 年之前
父節點
當前提交
953bb067da
共有 30 個文件被更改,包括 1355 次插入918 次删除
  1. 1 1
      Cargo.lock
  2. 1 1
      runtime-modules/common/src/lib.rs
  3. 26 0
      runtime-modules/common/src/origin.rs
  4. 0 5
      runtime-modules/common/src/origin_validator.rs
  5. 7 1
      runtime-modules/hiring/src/hiring/staking_policy.rs
  6. 3 22
      runtime-modules/proposals/codex/src/lib.rs
  7. 1 1
      runtime-modules/proposals/codex/src/tests/mock.rs
  8. 1 1
      runtime-modules/proposals/discussion/src/lib.rs
  9. 1 1
      runtime-modules/proposals/engine/src/lib.rs
  10. 1 1
      runtime-modules/proposals/engine/src/tests/mock/mod.rs
  11. 3 3
      runtime-modules/recurring-reward/src/lib.rs
  12. 6 1
      runtime-modules/service-discovery/src/mock.rs
  13. 1 1
      runtime-modules/storage/src/data_directory.rs
  14. 18 6
      runtime-modules/storage/src/tests/data_object_type_registry.rs
  15. 7 2
      runtime-modules/storage/src/tests/mock.rs
  16. 13 0
      runtime-modules/working-group/src/errors.rs
  17. 304 259
      runtime-modules/working-group/src/lib.rs
  18. 241 157
      runtime-modules/working-group/src/tests/fixtures.rs
  19. 200 0
      runtime-modules/working-group/src/tests/hiring_workflow.rs
  20. 55 3
      runtime-modules/working-group/src/tests/mock.rs
  21. 221 271
      runtime-modules/working-group/src/tests/mod.rs
  22. 29 35
      runtime-modules/working-group/src/types.rs
  23. 1 2
      runtime/Cargo.toml
  24. 141 0
      runtime/src/integration/content_working_group.rs
  25. 2 0
      runtime/src/integration/mod.rs
  26. 2 2
      runtime/src/integration/proposals/council_origin_validator.rs
  27. 2 2
      runtime/src/integration/proposals/membership_origin_validator.rs
  28. 1 1
      runtime/src/integration/storage.rs
  29. 49 0
      runtime/src/integration/working_group.rs
  30. 17 139
      runtime/src/lib.rs

+ 1 - 1
Cargo.lock

@@ -1614,7 +1614,7 @@ dependencies = [
 
 [[package]]
 name = "joystream-node-runtime"
-version = "6.16.0"
+version = "6.17.0"
 dependencies = [
  "parity-scale-codec",
  "safe-mix",

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

@@ -3,7 +3,7 @@
 
 pub mod constraints;
 pub mod currency;
-pub mod origin_validator;
+pub mod origin;
 
 use codec::{Decode, Encode};
 #[cfg(feature = "std")]

+ 26 - 0
runtime-modules/common/src/origin.rs

@@ -0,0 +1,26 @@
+use system::RawOrigin;
+
+/// Abstract validator for the origin(account_id) and actor_id (eg.: thread author id).
+pub trait ActorOriginValidator<Origin, ActorId, AccountId> {
+    /// Check for valid combination of origin and actor_id.
+    fn ensure_actor_origin(origin: Origin, actor_id: ActorId) -> Result<AccountId, &'static str>;
+}
+
+// Multiplies the T::Origin.
+// In our current substrate version system::Origin doesn't support clone(),
+// but it will be supported in latest up-to-date substrate version.
+// TODO: delete when T::Origin will support the clone()
+pub fn double_origin<T: system::Trait>(origin: T::Origin) -> (T::Origin, T::Origin) {
+    let coerced_origin = origin.into().ok().unwrap_or(RawOrigin::None);
+
+    let (cloned_origin1, cloned_origin2) = match coerced_origin {
+        RawOrigin::None => (RawOrigin::None, RawOrigin::None),
+        RawOrigin::Root => (RawOrigin::Root, RawOrigin::Root),
+        RawOrigin::Signed(account_id) => (
+            RawOrigin::Signed(account_id.clone()),
+            RawOrigin::Signed(account_id),
+        ),
+    };
+
+    (cloned_origin1.into(), cloned_origin2.into())
+}

+ 0 - 5
runtime-modules/common/src/origin_validator.rs

@@ -1,5 +0,0 @@
-/// Abstract validator for the origin(account_id) and actor_id (eg.: thread author id).
-pub trait ActorOriginValidator<Origin, ActorId, AccountId> {
-    /// Check for valid combination of origin and actor_id.
-    fn ensure_actor_origin(origin: Origin, actor_id: ActorId) -> Result<AccountId, &'static str>;
-}

+ 7 - 1
runtime-modules/hiring/src/hiring/staking_policy.rs

@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
 
 /// Policy for staking
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Eq, PartialEq, Clone)]
+#[derive(Encode, Decode, Debug, Eq, PartialEq, Clone, Default)]
 pub struct StakingPolicy<Balance, BlockNumber> {
     /// Staking amount
     pub amount: Balance,
@@ -77,3 +77,9 @@ pub enum StakingAmountLimitMode {
     /// Stake should be equal to provided value
     Exact,
 }
+
+impl Default for StakingAmountLimitMode {
+    fn default() -> Self {
+        StakingAmountLimitMode::Exact
+    }
+}

+ 3 - 22
runtime-modules/proposals/codex/src/lib.rs

@@ -54,7 +54,7 @@ mod proposal_types;
 #[cfg(test)]
 mod tests;
 
-use common::origin_validator::ActorOriginValidator;
+use common::origin::ActorOriginValidator;
 use governance::election_params::ElectionParameters;
 use proposal_engine::ProposalParameters;
 use rstd::clone::Clone;
@@ -65,7 +65,7 @@ use sr_primitives::traits::Zero;
 use srml_support::dispatch::DispatchResult;
 use srml_support::traits::{Currency, Get};
 use srml_support::{decl_error, decl_module, decl_storage, ensure, print};
-use system::{ensure_root, RawOrigin};
+use system::ensure_root;
 
 pub use crate::proposal_types::ProposalsConfigParameters;
 pub use proposal_types::{ProposalDetails, ProposalDetailsOf, ProposalEncoder};
@@ -577,7 +577,7 @@ decl_module! {
             origin,
             wasm: Vec<u8>,
         ) {
-            let (cloned_origin1, cloned_origin2) =  Self::double_origin(origin);
+            let (cloned_origin1, cloned_origin2) = common::origin::double_origin::<T>(origin);
             ensure_root(cloned_origin1)?;
 
             print("Runtime upgrade proposal execution started.");
@@ -590,25 +590,6 @@ decl_module! {
 }
 
 impl<T: Trait> Module<T> {
-    // Multiplies the T::Origin.
-    // In our current substrate version system::Origin doesn't support clone(),
-    // but it will be supported in latest up-to-date substrate version.
-    // TODO: delete when T::Origin will support the clone()
-    fn double_origin(origin: T::Origin) -> (T::Origin, T::Origin) {
-        let coerced_origin = origin.into().ok().unwrap_or(RawOrigin::None);
-
-        let (cloned_origin1, cloned_origin2) = match coerced_origin {
-            RawOrigin::None => (RawOrigin::None, RawOrigin::None),
-            RawOrigin::Root => (RawOrigin::Root, RawOrigin::Root),
-            RawOrigin::Signed(account_id) => (
-                RawOrigin::Signed(account_id.clone()),
-                RawOrigin::Signed(account_id),
-            ),
-        };
-
-        (cloned_origin1.into(), cloned_origin2.into())
-    }
-
     // Generic template proposal builder
     fn create_proposal(
         origin: T::Origin,

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

@@ -123,7 +123,7 @@ impl governance::council::Trait for Test {
     type CouncilTermEnded = ();
 }
 
-impl common::origin_validator::ActorOriginValidator<Origin, u64, u64> for () {
+impl common::origin::ActorOriginValidator<Origin, u64, u64> for () {
     fn ensure_actor_origin(origin: Origin, _: u64) -> Result<u64, &'static str> {
         let account_id = system::ensure_signed(origin)?;
 

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

@@ -56,7 +56,7 @@ use srml_support::{decl_error, decl_event, decl_module, decl_storage, ensure, Pa
 use srml_support::traits::Get;
 use types::{DiscussionPost, DiscussionThread, ThreadCounter};
 
-use common::origin_validator::ActorOriginValidator;
+use common::origin::ActorOriginValidator;
 use srml_support::dispatch::DispatchResult;
 
 type MemberId<T> = <T as membership::members::Trait>::MemberId;

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

@@ -135,7 +135,7 @@ use srml_support::{
 use system::{ensure_root, RawOrigin};
 
 use crate::types::ApprovedProposalData;
-use common::origin_validator::ActorOriginValidator;
+use common::origin::ActorOriginValidator;
 use srml_support::dispatch::Dispatchable;
 
 type MemberId<T> = <T as membership::members::Trait>::MemberId;

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

@@ -124,7 +124,7 @@ impl Default for proposals::Call<Test> {
     }
 }
 
-impl common::origin_validator::ActorOriginValidator<Origin, u64, u64> 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 = system::ensure_signed(origin)?;
 

+ 3 - 3
runtime-modules/recurring-reward/src/lib.rs

@@ -94,10 +94,10 @@ pub struct RewardRelationship<AccountId, Balance, BlockNumber, MintId, Recipient
     mint_id: MintId,
 
     /// Destination account for reward
-    account: AccountId,
+    pub account: AccountId,
 
     /// The payout amount at the next payout
-    amount_per_payout: Balance,
+    pub amount_per_payout: Balance,
 
     /// When set, identifies block when next payout should be processed,
     /// otherwise there is no pending payout
@@ -146,7 +146,7 @@ decl_storage! {
 
         RecipientsCreated get(recipients_created): T::RecipientId;
 
-        RewardRelationships get(reward_relationships): linked_map T::RewardRelationshipId => RewardRelationship<T::AccountId, BalanceOf<T>, T::BlockNumber, T::MintId, T::RecipientId>;
+        pub RewardRelationships get(reward_relationships): linked_map T::RewardRelationshipId => RewardRelationship<T::AccountId, BalanceOf<T>, T::BlockNumber, T::MintId, T::RecipientId>;
 
         RewardRelationshipsCreated get(reward_relationships_created): T::RewardRelationshipId;
     }

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

@@ -130,8 +130,13 @@ impl recurringrewards::Trait for Test {
     type RewardRelationshipId = u64;
 }
 
+parameter_types! {
+    pub const MaxWorkerNumberLimit: u32 = 3;
+}
+
 impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = MetaEvent;
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
 }
 
 impl timestamp::Trait for Test {
@@ -158,7 +163,7 @@ pub(crate) fn hire_storage_provider() -> (u64, u64) {
 
     let storage_provider = working_group::Worker {
         member_id: 1,
-        role_account: role_account_id,
+        role_account_id,
         reward_relationship: None,
         role_stake_profile: None,
     };

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

@@ -27,7 +27,7 @@ use sr_primitives::traits::{MaybeSerialize, Member};
 use srml_support::{decl_error, decl_event, decl_module, decl_storage, ensure, Parameter};
 use system::{self, ensure_root};
 
-use common::origin_validator::ActorOriginValidator;
+use common::origin::ActorOriginValidator;
 pub(crate) use common::BlockAndTime;
 
 use crate::data_object_type_registry;

+ 18 - 6
runtime-modules/storage/src/tests/data_object_type_registry.rs

@@ -1,21 +1,33 @@
 #![cfg(test)]
 
 use super::mock::*;
-use crate::StorageWorkingGroup;
+use srml_support::{StorageLinkedMap, StorageValue};
 use system::{self, EventRecord, Phase, RawOrigin};
 
 const DEFAULT_LEADER_ACCOUNT_ID: u64 = 1;
 const DEFAULT_LEADER_MEMBER_ID: u64 = 1;
+const DEFAULT_LEADER_WORKER_ID: u32 = 1;
 
 struct SetLeadFixture;
 impl SetLeadFixture {
     fn set_default_lead() {
-        let set_lead_result = <StorageWorkingGroup<Test>>::set_lead(
-            RawOrigin::Root.into(),
-            DEFAULT_LEADER_MEMBER_ID,
-            DEFAULT_LEADER_ACCOUNT_ID,
+        let worker = working_group::Worker {
+            member_id: DEFAULT_LEADER_MEMBER_ID,
+            role_account_id: DEFAULT_LEADER_ACCOUNT_ID,
+            reward_relationship: None,
+            role_stake_profile: None,
+        };
+
+        // Create the worker.
+        <working_group::WorkerById<Test, StorageWorkingGroupInstance>>::insert(
+            DEFAULT_LEADER_WORKER_ID,
+            worker,
+        );
+
+        // Update current lead.
+        <working_group::CurrentLead<Test, StorageWorkingGroupInstance>>::put(
+            DEFAULT_LEADER_WORKER_ID,
         );
-        assert!(set_lead_result.is_ok());
     }
 }
 

+ 7 - 2
runtime-modules/storage/src/tests/mock.rs

@@ -146,8 +146,13 @@ impl GovernanceCurrency for Test {
     type Currency = balances::Module<Self>;
 }
 
+parameter_types! {
+    pub const MaxWorkerNumberLimit: u32 = 3;
+}
+
 impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = MetaEvent;
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
 }
 
 impl data_object_type_registry::Trait for Test {
@@ -169,7 +174,7 @@ impl crate::data_directory::StorageProviderHelper<Test> for () {
     }
 }
 
-impl common::origin_validator::ActorOriginValidator<Origin, u64, u64> 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 = system::ensure_signed(origin)?;
 
@@ -304,7 +309,7 @@ pub(crate) fn hire_storage_provider() -> (u64, u32) {
 
     let storage_provider = working_group::Worker {
         member_id: 1,
-        role_account: role_account_id,
+        role_account_id,
         reward_relationship: None,
         role_stake_profile: None,
     };

+ 13 - 0
runtime-modules/working-group/src/errors.rs

@@ -14,6 +14,12 @@ decl_error! {
         /// 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,
 
@@ -235,6 +241,12 @@ decl_error! {
 
         /// Require root origin in extrinsics.
         RequireRootOrigin,
+
+        /// Require signed origin in extrinsics.
+        RequireSignedOrigin,
+
+        /// Working group size limit exceeded.
+        MaxActiveWorkerNumberExceeded,
     }
 }
 
@@ -243,6 +255,7 @@ impl From<system::Error> for Error {
         match error {
             system::Error::Other(msg) => Error::Other(msg),
             system::Error::RequireRootOrigin => Error::RequireRootOrigin,
+            system::Error::RequireSignedOrigin => Error::RequireSignedOrigin,
             _ => Error::Other(error.into()),
         }
     }

文件差異過大導致無法顯示
+ 304 - 259
runtime-modules/working-group/src/lib.rs


+ 241 - 157
runtime-modules/working-group/src/tests/fixtures.rs

@@ -1,6 +1,7 @@
 use super::mock::{
     Balances, Membership, System, Test, TestEvent, TestWorkingGroup, TestWorkingGroupInstance,
 };
+use crate::tests::fill_worker_position;
 use crate::types::{
     Application, Opening, OpeningPolicyCommitment, OpeningType, RewardPolicy, RoleStakeProfile,
     Worker,
@@ -22,7 +23,7 @@ pub struct IncreaseWorkerStakeFixture {
 impl IncreaseWorkerStakeFixture {
     pub fn default_for_worker_id(worker_id: u64) -> Self {
         let account_id = 1;
-        IncreaseWorkerStakeFixture {
+        Self {
             origin: RawOrigin::Signed(1),
             worker_id,
             balance: 10,
@@ -30,11 +31,11 @@ impl IncreaseWorkerStakeFixture {
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        IncreaseWorkerStakeFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn with_balance(self, balance: u64) -> Self {
-        IncreaseWorkerStakeFixture { balance, ..self }
+        Self { balance, ..self }
     }
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {
@@ -71,11 +72,12 @@ pub struct TerminateWorkerRoleFixture {
     origin: RawOrigin<u64>,
     text: Vec<u8>,
     constraint: InputValidationLengthConstraint,
+    slash_stake: bool,
 }
 
 impl TerminateWorkerRoleFixture {
     pub fn default_for_worker_id(worker_id: u64) -> Self {
-        TerminateWorkerRoleFixture {
+        Self {
             worker_id,
             origin: RawOrigin::Signed(1),
             text: b"rationale_text".to_vec(),
@@ -83,14 +85,22 @@ impl TerminateWorkerRoleFixture {
                 min: 1,
                 max_min_diff: 20,
             },
+            slash_stake: false,
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        TerminateWorkerRoleFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn with_text(self, text: Vec<u8>) -> Self {
-        TerminateWorkerRoleFixture { text, ..self }
+        Self { text, ..self }
+    }
+
+    pub fn with_slashing(self) -> Self {
+        Self {
+            slash_stake: true,
+            ..self
+        }
     }
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {
@@ -100,6 +110,7 @@ impl TerminateWorkerRoleFixture {
             self.origin.clone().into(),
             self.worker_id,
             self.text.clone(),
+            self.slash_stake,
         );
         assert_eq!(actual_result, expected_result);
 
@@ -120,13 +131,13 @@ pub(crate) struct LeaveWorkerRoleFixture {
 
 impl LeaveWorkerRoleFixture {
     pub fn default_for_worker_id(worker_id: u64) -> Self {
-        LeaveWorkerRoleFixture {
+        Self {
             worker_id,
             origin: RawOrigin::Signed(1),
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        LeaveWorkerRoleFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {
@@ -152,54 +163,72 @@ pub struct UpdateWorkerRewardAmountFixture {
 
 impl UpdateWorkerRewardAmountFixture {
     pub fn default_for_worker_id(worker_id: u64) -> Self {
-        UpdateWorkerRewardAmountFixture {
+        let lead_account_id = get_current_lead_account_id();
+
+        Self {
             worker_id,
-            amount: 100,
-            origin: RawOrigin::Signed(1),
+            amount: 120,
+            origin: RawOrigin::Signed(lead_account_id),
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        UpdateWorkerRewardAmountFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {
-        assert_eq!(
-            TestWorkingGroup::update_reward_amount(
-                self.origin.clone().into(),
-                self.worker_id,
-                self.amount
-            ),
-            expected_result
+        let actual_result = TestWorkingGroup::update_reward_amount(
+            self.origin.clone().into(),
+            self.worker_id,
+            self.amount,
         );
+
+        assert_eq!(actual_result.clone(), expected_result);
+
+        if actual_result.is_ok() {
+            let worker = TestWorkingGroup::worker_by_id(self.worker_id);
+            let relationship_id = worker.reward_relationship.unwrap();
+
+            let relationship = recurringrewards::RewardRelationships::<Test>::get(relationship_id);
+
+            assert_eq!(relationship.amount_per_payout, self.amount);
+        }
     }
 }
 pub struct UpdateWorkerRewardAccountFixture {
     worker_id: u64,
-    new_role_account_id: u64,
+    new_reward_account_id: u64,
     origin: RawOrigin<u64>,
 }
 
 impl UpdateWorkerRewardAccountFixture {
-    pub fn default_with_ids(worker_id: u64, new_role_account_id: u64) -> Self {
-        UpdateWorkerRewardAccountFixture {
+    pub fn default_with_ids(worker_id: u64, new_reward_account_id: u64) -> Self {
+        Self {
             worker_id,
-            new_role_account_id,
+            new_reward_account_id,
             origin: RawOrigin::Signed(1),
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        UpdateWorkerRewardAccountFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {
-        assert_eq!(
-            TestWorkingGroup::update_reward_account(
-                self.origin.clone().into(),
-                self.worker_id,
-                self.new_role_account_id
-            ),
-            expected_result
+        let actual_result = TestWorkingGroup::update_reward_account(
+            self.origin.clone().into(),
+            self.worker_id,
+            self.new_reward_account_id,
         );
+
+        assert_eq!(actual_result.clone(), expected_result);
+
+        if actual_result.is_ok() {
+            let worker = TestWorkingGroup::worker_by_id(self.worker_id);
+            let relationship_id = worker.reward_relationship.unwrap();
+
+            let relationship = recurringrewards::RewardRelationships::<Test>::get(relationship_id);
+
+            assert_eq!(relationship.account, self.new_reward_account_id);
+        }
     }
 }
 
@@ -211,14 +240,14 @@ pub struct UpdateWorkerRoleAccountFixture {
 
 impl UpdateWorkerRoleAccountFixture {
     pub fn default_with_ids(worker_id: u64, new_role_account_id: u64) -> Self {
-        UpdateWorkerRoleAccountFixture {
+        Self {
             worker_id,
             new_role_account_id,
             origin: RawOrigin::Signed(1),
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        UpdateWorkerRoleAccountFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {
@@ -232,22 +261,11 @@ impl UpdateWorkerRoleAccountFixture {
         if actual_result.is_ok() {
             let worker = TestWorkingGroup::worker_by_id(self.worker_id);
 
-            assert_eq!(worker.role_account, self.new_role_account_id);
+            assert_eq!(worker.role_account_id, self.new_role_account_id);
         }
     }
 }
 
-pub struct UnsetLeadFixture;
-impl UnsetLeadFixture {
-    pub fn unset_lead() {
-        assert_eq!(TestWorkingGroup::unset_lead(RawOrigin::Root.into()), Ok(()));
-    }
-
-    pub fn call_and_assert(origin: RawOrigin<u64>, expected_result: Result<(), Error>) {
-        assert_eq!(TestWorkingGroup::unset_lead(origin.into()), expected_result);
-    }
-}
-
 pub fn set_mint_id(mint_id: u64) {
     <crate::Mint<Test, TestWorkingGroupInstance>>::put(mint_id);
 }
@@ -260,7 +278,7 @@ pub struct FillWorkerOpeningFixture {
     origin: RawOrigin<u64>,
     opening_id: u64,
     successful_application_ids: BTreeSet<u64>,
-    role_account: u64,
+    role_account_id: u64,
     reward_policy: Option<RewardPolicy<u64, u64>>,
 }
 
@@ -268,34 +286,41 @@ impl FillWorkerOpeningFixture {
     pub fn default_for_ids(opening_id: u64, application_ids: Vec<u64>) -> Self {
         let application_ids: BTreeSet<u64> = application_ids.iter().map(|x| *x).collect();
 
-        FillWorkerOpeningFixture {
+        Self {
             origin: RawOrigin::Signed(1),
             opening_id,
             successful_application_ids: application_ids,
-            role_account: 1,
+            role_account_id: 1,
             reward_policy: None,
         }
     }
 
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        FillWorkerOpeningFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn with_reward_policy(self, reward_policy: RewardPolicy<u64, u64>) -> Self {
-        FillWorkerOpeningFixture {
+        Self {
             reward_policy: Some(reward_policy),
             ..self
         }
     }
 
-    pub fn call_and_assert(&self, expected_result: Result<(), Error>) -> u64 {
+    pub fn call(&self) -> Result<u64, Error> {
         let saved_worker_next_id = TestWorkingGroup::next_worker_id();
-        let actual_result = TestWorkingGroup::fill_opening(
+        TestWorkingGroup::fill_opening(
             self.origin.clone().into(),
             self.opening_id,
             self.successful_application_ids.clone(),
             self.reward_policy.clone(),
-        );
+        )?;
+
+        Ok(saved_worker_next_id)
+    }
+
+    pub fn call_and_assert(&self, expected_result: Result<(), Error>) -> u64 {
+        let saved_worker_next_id = TestWorkingGroup::next_worker_id();
+        let actual_result = self.call().map(|_| ());
         assert_eq!(actual_result.clone(), expected_result);
 
         if actual_result.is_ok() {
@@ -315,10 +340,8 @@ impl FillWorkerOpeningFixture {
                     &stake_id,
                     &opening
                         .policy_commitment
-                        .terminate_worker_role_stake_unstaking_period,
-                    &opening
-                        .policy_commitment
-                        .exit_worker_role_stake_unstaking_period,
+                        .terminate_role_stake_unstaking_period,
+                    &opening.policy_commitment.exit_role_stake_unstaking_period,
                 ))
             } else {
                 None
@@ -327,7 +350,7 @@ impl FillWorkerOpeningFixture {
 
             let expected_worker = Worker {
                 member_id: 1,
-                role_account: self.role_account,
+                role_account_id: self.role_account_id,
                 reward_relationship,
                 role_stake_profile,
             };
@@ -348,13 +371,13 @@ pub struct BeginReviewWorkerApplicationsFixture {
 
 impl BeginReviewWorkerApplicationsFixture {
     pub fn default_for_opening_id(opening_id: u64) -> Self {
-        BeginReviewWorkerApplicationsFixture {
+        Self {
             origin: RawOrigin::Signed(1),
             opening_id,
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        BeginReviewWorkerApplicationsFixture { origin, ..self }
+        Self { origin, ..self }
     }
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {
         let actual_result =
@@ -370,16 +393,16 @@ pub struct TerminateApplicationFixture {
 
 impl TerminateApplicationFixture {
     pub fn with_signer(self, account_id: u64) -> Self {
-        TerminateApplicationFixture {
+        Self {
             origin: RawOrigin::Signed(account_id),
             ..self
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        TerminateApplicationFixture { origin, ..self }
+        Self { origin, ..self }
     }
     pub fn default_for_application_id(application_id: u64) -> Self {
-        TerminateApplicationFixture {
+        Self {
             origin: RawOrigin::Signed(1),
             worker_application_id: application_id,
         }
@@ -399,16 +422,16 @@ pub struct WithdrawApplicationFixture {
 
 impl WithdrawApplicationFixture {
     pub fn with_signer(self, account_id: u64) -> Self {
-        WithdrawApplicationFixture {
+        Self {
             origin: RawOrigin::Signed(account_id),
             ..self
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        WithdrawApplicationFixture { origin, ..self }
+        Self { origin, ..self }
     }
     pub fn default_for_application_id(application_id: u64) -> Self {
-        WithdrawApplicationFixture {
+        Self {
             origin: RawOrigin::Signed(1),
             worker_application_id: application_id,
         }
@@ -427,6 +450,10 @@ pub fn increase_total_balance_issuance_using_account_id(account_id: u64, balance
         <Balances as srml_support::traits::Currency<u64>>::deposit_creating(&account_id, balance);
 }
 
+pub fn get_balance(account_id: u64) -> u64 {
+    <super::mock::Balances as srml_support::traits::Currency<u64>>::total_balance(&account_id)
+}
+
 pub fn setup_members(count: u8) {
     let authority_account_id = 1;
     Membership::set_screening_authority(RawOrigin::Root.into(), authority_account_id).unwrap();
@@ -451,7 +478,7 @@ pub struct ApplyOnWorkerOpeningFixture {
     origin: RawOrigin<u64>,
     member_id: u64,
     worker_opening_id: u64,
-    role_account: u64,
+    role_account_id: u64,
     opt_role_stake_balance: Option<u64>,
     opt_application_stake_balance: Option<u64>,
     human_readable_text: Vec<u8>,
@@ -459,49 +486,64 @@ pub struct ApplyOnWorkerOpeningFixture {
 
 impl ApplyOnWorkerOpeningFixture {
     pub fn with_text(self, text: Vec<u8>) -> Self {
-        ApplyOnWorkerOpeningFixture {
+        Self {
             human_readable_text: text,
             ..self
         }
     }
 
-    pub fn with_role_stake(self, stake: u64) -> Self {
-        ApplyOnWorkerOpeningFixture {
-            opt_role_stake_balance: Some(stake),
+    pub fn with_origin(self, origin: RawOrigin<u64>, member_id: u64) -> Self {
+        Self {
+            origin,
+            member_id,
+            ..self
+        }
+    }
+
+    pub fn with_role_stake(self, stake: Option<u64>) -> Self {
+        Self {
+            opt_role_stake_balance: stake,
             ..self
         }
     }
 
     pub fn with_application_stake(self, stake: u64) -> Self {
-        ApplyOnWorkerOpeningFixture {
+        Self {
             opt_application_stake_balance: Some(stake),
             ..self
         }
     }
 
     pub fn default_for_opening_id(opening_id: u64) -> Self {
-        ApplyOnWorkerOpeningFixture {
+        Self {
             origin: RawOrigin::Signed(1),
             member_id: 1,
             worker_opening_id: opening_id,
-            role_account: 1,
+            role_account_id: 1,
             opt_role_stake_balance: None,
             opt_application_stake_balance: None,
             human_readable_text: b"human_text".to_vec(),
         }
     }
 
-    pub fn call_and_assert(&self, expected_result: Result<(), Error>) -> u64 {
+    pub fn call(&self) -> Result<u64, Error> {
         let saved_application_next_id = TestWorkingGroup::next_application_id();
-        let actual_result = TestWorkingGroup::apply_on_opening(
+        TestWorkingGroup::apply_on_opening(
             self.origin.clone().into(),
             self.member_id,
             self.worker_opening_id,
-            self.role_account,
+            self.role_account_id,
             self.opt_role_stake_balance,
             self.opt_application_stake_balance,
             self.human_readable_text.clone(),
-        );
+        )?;
+
+        Ok(saved_application_next_id)
+    }
+    pub fn call_and_assert(&self, expected_result: Result<(), Error>) -> u64 {
+        let saved_application_next_id = TestWorkingGroup::next_application_id();
+
+        let actual_result = self.call().map(|_| ());
         assert_eq!(actual_result.clone(), expected_result);
 
         if actual_result.is_ok() {
@@ -514,7 +556,7 @@ impl ApplyOnWorkerOpeningFixture {
             let actual_application = TestWorkingGroup::application_by_id(application_id);
 
             let expected_application = Application {
-                role_account: self.role_account,
+                role_account_id: self.role_account_id,
                 opening_id: self.worker_opening_id,
                 member_id: self.member_id,
                 hiring_application_id: application_id,
@@ -537,7 +579,7 @@ pub struct AcceptWorkerApplicationsFixture {
 
 impl AcceptWorkerApplicationsFixture {
     pub fn default_for_opening_id(opening_id: u64) -> Self {
-        AcceptWorkerApplicationsFixture {
+        Self {
             origin: RawOrigin::Signed(1),
             opening_id,
         }
@@ -550,27 +592,82 @@ impl AcceptWorkerApplicationsFixture {
     }
 }
 
-pub struct SetLeadFixture;
+pub struct SetLeadFixture {
+    pub member_id: u64,
+    pub role_account_id: u64,
+    pub worker_id: u64,
+}
+impl Default for SetLeadFixture {
+    fn default() -> Self {
+        SetLeadFixture {
+            member_id: 1,
+            role_account_id: 1,
+            worker_id: 1,
+        }
+    }
+}
+
 impl SetLeadFixture {
-    pub fn set_lead(lead_account_id: u64) {
-        assert_eq!(
-            TestWorkingGroup::set_lead(RawOrigin::Root.into(), 1, lead_account_id),
-            Ok(())
-        );
+    pub fn unset_lead() {
+        TestWorkingGroup::unset_lead();
     }
 
-    pub fn call_and_assert(
-        origin: RawOrigin<u64>,
-        member_id: u64,
-        account_id: u64,
-        expected_result: Result<(), Error>,
-    ) {
-        assert_eq!(
-            TestWorkingGroup::set_lead(origin.into(), member_id, account_id),
-            expected_result
-        );
+    pub fn set_lead(self) {
+        TestWorkingGroup::set_lead(self.worker_id);
+    }
+    pub fn set_lead_with_ids(member_id: u64, role_account_id: u64, worker_id: u64) {
+        Self {
+            member_id,
+            role_account_id,
+            worker_id,
+        }
+        .set_lead();
+    }
+}
+
+pub struct HireLeadFixture {
+    setup_environment: bool,
+    stake: Option<u64>,
+    reward_policy: Option<RewardPolicy<u64, u64>>,
+}
+
+impl Default for HireLeadFixture {
+    fn default() -> Self {
+        Self {
+            setup_environment: true,
+            stake: None,
+            reward_policy: None,
+        }
     }
 }
+impl HireLeadFixture {
+    pub fn with_stake(self, stake: u64) -> Self {
+        Self {
+            stake: Some(stake),
+            ..self
+        }
+    }
+    pub fn with_reward_policy(self, reward_policy: RewardPolicy<u64, u64>) -> Self {
+        Self {
+            reward_policy: Some(reward_policy),
+            ..self
+        }
+    }
+
+    pub fn hire_lead(self) -> u64 {
+        fill_worker_position(
+            self.reward_policy,
+            self.stake,
+            self.setup_environment,
+            OpeningType::Leader,
+            Some(b"leader".to_vec()),
+        )
+    }
+}
+
+pub fn get_worker_by_id(worker_id: u64) -> Worker<u64, u64, u64, u64, u64> {
+    TestWorkingGroup::worker_by_id(worker_id)
+}
 
 pub struct AddWorkerOpeningFixture {
     origin: RawOrigin<u64>,
@@ -582,7 +679,7 @@ pub struct AddWorkerOpeningFixture {
 
 impl Default for AddWorkerOpeningFixture {
     fn default() -> Self {
-        AddWorkerOpeningFixture {
+        Self {
             origin: RawOrigin::Signed(1),
             activate_at: hiring::ActivateOpeningAt::CurrentBlock,
             commitment: <OpeningPolicyCommitment<u64, u64>>::default(),
@@ -597,7 +694,7 @@ impl AddWorkerOpeningFixture {
         self,
         policy_commitment: OpeningPolicyCommitment<u64, u64>,
     ) -> Self {
-        AddWorkerOpeningFixture {
+        Self {
             commitment: policy_commitment,
             ..self
         }
@@ -605,13 +702,8 @@ impl AddWorkerOpeningFixture {
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) -> u64 {
         let saved_opening_next_id = TestWorkingGroup::next_opening_id();
-        let actual_result = TestWorkingGroup::add_opening(
-            self.origin.clone().into(),
-            self.activate_at.clone(),
-            self.commitment.clone(),
-            self.human_readable_text.clone(),
-            self.opening_type,
-        );
+        let actual_result = self.call().map(|_| ());
+
         assert_eq!(actual_result.clone(), expected_result);
 
         if actual_result.is_ok() {
@@ -624,7 +716,7 @@ impl AddWorkerOpeningFixture {
             let actual_opening = TestWorkingGroup::opening_by_id(opening_id);
 
             let expected_opening = Opening::<u64, u64, u64, u64> {
-                opening_id,
+                hiring_opening_id: opening_id,
                 applications: BTreeSet::new(),
                 policy_commitment: self.commitment.clone(),
                 opening_type: self.opening_type,
@@ -636,26 +728,39 @@ impl AddWorkerOpeningFixture {
         saved_opening_next_id
     }
 
+    pub fn call(&self) -> Result<u64, Error> {
+        let saved_opening_next_id = TestWorkingGroup::next_opening_id();
+        TestWorkingGroup::add_opening(
+            self.origin.clone().into(),
+            self.activate_at.clone(),
+            self.commitment.clone(),
+            self.human_readable_text.clone(),
+            self.opening_type,
+        )?;
+
+        Ok(saved_opening_next_id)
+    }
+
     pub fn with_text(self, text: Vec<u8>) -> Self {
-        AddWorkerOpeningFixture {
+        Self {
             human_readable_text: text,
             ..self
         }
     }
 
     pub fn with_opening_type(self, opening_type: OpeningType) -> Self {
-        AddWorkerOpeningFixture {
+        Self {
             opening_type,
             ..self
         }
     }
 
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        AddWorkerOpeningFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn with_activate_at(self, activate_at: hiring::ActivateOpeningAt<u64>) -> Self {
-        AddWorkerOpeningFixture {
+        Self {
             activate_at,
             ..self
         }
@@ -664,43 +769,6 @@ impl AddWorkerOpeningFixture {
 
 pub struct EventFixture;
 impl EventFixture {
-    pub fn assert_crate_events(
-        expected_raw_events: Vec<
-            RawEvent<
-                u64,
-                u64,
-                u64,
-                u64,
-                u64,
-                u64,
-                std::collections::BTreeMap<u64, u64>,
-                Vec<u8>,
-                u64,
-                u64,
-                TestWorkingGroupInstance,
-            >,
-        >,
-    ) {
-        let converted_events = expected_raw_events
-            .iter()
-            .map(|ev| TestEvent::working_group_TestWorkingGroupInstance(ev.clone()))
-            .collect::<Vec<TestEvent>>();
-
-        Self::assert_global_events(converted_events)
-    }
-    pub fn assert_global_events(expected_raw_events: Vec<TestEvent>) {
-        let expected_events = expected_raw_events
-            .iter()
-            .map(|ev| EventRecord {
-                phase: Phase::ApplyExtrinsic(0),
-                event: ev.clone(),
-                topics: vec![],
-            })
-            .collect::<Vec<EventRecord<_, _>>>();
-
-        assert_eq!(System::events(), expected_events);
-    }
-
     pub fn assert_last_crate_event(
         expected_raw_event: RawEvent<
             u64,
@@ -708,7 +776,6 @@ impl EventFixture {
             u64,
             u64,
             u64,
-            u64,
             std::collections::BTreeMap<u64, u64>,
             Vec<u8>,
             u64,
@@ -742,19 +809,22 @@ pub struct DecreaseWorkerStakeFixture {
 impl DecreaseWorkerStakeFixture {
     pub fn default_for_worker_id(worker_id: u64) -> Self {
         let account_id = 1;
-        DecreaseWorkerStakeFixture {
-            origin: RawOrigin::Signed(account_id),
+
+        let lead_account_id = get_current_lead_account_id();
+
+        Self {
+            origin: RawOrigin::Signed(lead_account_id),
             worker_id,
             balance: 10,
             account_id,
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        DecreaseWorkerStakeFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn with_balance(self, balance: u64) -> Self {
-        DecreaseWorkerStakeFixture { balance, ..self }
+        Self { balance, ..self }
     }
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {
@@ -786,7 +856,7 @@ impl DecreaseWorkerStakeFixture {
     }
 }
 
-fn get_stake_balance(stake: stake::Stake<u64, u64, u64>) -> u64 {
+pub(crate) fn get_stake_balance(stake: stake::Stake<u64, u64, u64>) -> u64 {
     if let stake::StakingStatus::Staked(stake) = stake.staking_status {
         return stake.staked_amount;
     }
@@ -794,6 +864,17 @@ fn get_stake_balance(stake: stake::Stake<u64, u64, u64>) -> u64 {
     panic!("Not staked.");
 }
 
+fn get_current_lead_account_id() -> u64 {
+    let leader_worker_id = TestWorkingGroup::current_lead();
+
+    if let Some(leader_worker_id) = leader_worker_id {
+        let leader = TestWorkingGroup::worker_by_id(leader_worker_id);
+        leader.role_account_id
+    } else {
+        0 // return invalid lead_account_id for testing
+    }
+}
+
 pub struct SlashWorkerStakeFixture {
     origin: RawOrigin<u64>,
     worker_id: u64,
@@ -804,19 +885,22 @@ pub struct SlashWorkerStakeFixture {
 impl SlashWorkerStakeFixture {
     pub fn default_for_worker_id(worker_id: u64) -> Self {
         let account_id = 1;
-        SlashWorkerStakeFixture {
-            origin: RawOrigin::Signed(account_id),
+
+        let lead_account_id = get_current_lead_account_id();
+
+        Self {
+            origin: RawOrigin::Signed(lead_account_id),
             worker_id,
             balance: 10,
             account_id,
         }
     }
     pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
-        SlashWorkerStakeFixture { origin, ..self }
+        Self { origin, ..self }
     }
 
     pub fn with_balance(self, balance: u64) -> Self {
-        SlashWorkerStakeFixture { balance, ..self }
+        Self { balance, ..self }
     }
 
     pub fn call_and_assert(&self, expected_result: Result<(), Error>) {

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

@@ -0,0 +1,200 @@
+use crate::tests::fixtures::{
+    create_mint, increase_total_balance_issuance_using_account_id, set_mint_id, setup_members,
+    AddWorkerOpeningFixture, ApplyOnWorkerOpeningFixture, BeginReviewWorkerApplicationsFixture,
+    FillWorkerOpeningFixture, SetLeadFixture,
+};
+use crate::tests::mock::TestWorkingGroup;
+use crate::Error;
+use crate::{OpeningPolicyCommitment, OpeningType, RewardPolicy};
+use system::RawOrigin;
+
+#[derive(Clone)]
+struct HiringWorkflowApplication {
+    stake: Option<u64>,
+    worker_handle: Vec<u8>,
+    origin: RawOrigin<u64>,
+    member_id: u64,
+}
+
+pub struct HiringWorkflow {
+    opening_type: OpeningType,
+    expected_result: Result<(), Error>,
+    role_stake: Option<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,
+            expected_result: Ok(()),
+            role_stake: None,
+            applications: Vec::new(),
+            setup_environment: true,
+            reward_policy: None,
+        }
+    }
+}
+
+impl HiringWorkflow {
+    pub fn expect(self, result: Result<(), Error>) -> Self {
+        Self {
+            expected_result: result,
+            ..self
+        }
+    }
+
+    pub fn disable_setup_environment(self) -> Self {
+        Self {
+            setup_environment: false,
+            ..self
+        }
+    }
+
+    pub fn with_setup_environment(self, setup_environment: bool) -> Self {
+        Self {
+            setup_environment,
+            ..self
+        }
+    }
+
+    pub fn with_opening_type(self, opening_type: OpeningType) -> Self {
+        Self {
+            opening_type,
+            ..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 {
+        Self {
+            reward_policy,
+            ..self
+        }
+    }
+
+    pub fn add_default_application(self) -> Self {
+        let worker_handle = b"default worker handle".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)
+    }
+
+    pub fn add_application_with_origin(
+        self,
+        worker_handle: Vec<u8>,
+        origin: RawOrigin<u64>,
+        member_id: u64,
+    ) -> Self {
+        let mut applications = self.applications;
+        applications.push(HiringWorkflowApplication {
+            worker_handle,
+            stake: self.role_stake.clone(),
+            origin,
+            member_id,
+        });
+
+        Self {
+            applications,
+            ..self
+        }
+    }
+
+    fn setup_environment(&self) {
+        if matches!(self.opening_type, OpeningType::Worker) {
+            SetLeadFixture::default().set_lead();
+        }
+        increase_total_balance_issuance_using_account_id(1, 10000);
+        setup_members(4);
+        set_mint_id(create_mint());
+    }
+
+    pub fn execute(&self) -> Option<u64> {
+        if self.setup_environment {
+            self.setup_environment()
+        }
+
+        let result = self.fill_worker_position();
+
+        let check_result = result.clone().map(|_| ());
+
+        assert_eq!(check_result, self.expected_result);
+
+        result.ok()
+    }
+
+    fn fill_worker_position(&self) -> Result<u64, Error> {
+        let origin = match self.opening_type {
+            OpeningType::Leader => RawOrigin::Root,
+            OpeningType::Worker => {
+                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;
+
+                RawOrigin::Signed(lead_account_id)
+            }
+        };
+
+        // create the opening
+        let mut add_worker_opening_fixture = AddWorkerOpeningFixture::default()
+            .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)
+                    .with_text(application.worker_handle)
+                    .with_origin(application.origin, application.member_id)
+                    .with_role_stake(self.role_stake);
+
+            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 worker_id = fill_worker_opening_fixture.call()?;
+
+        Ok(worker_id)
+    }
+}

+ 55 - 3
runtime-modules/working-group/src/tests/mock.rs

@@ -1,4 +1,4 @@
-use crate::{Module, Trait};
+use crate::{BalanceOf, Module, NegativeImbalance, Trait};
 use common::constraints::InputValidationLengthConstraint;
 use primitives::H256;
 use sr_primitives::{
@@ -6,7 +6,10 @@ use sr_primitives::{
     traits::{BlakeTwo256, IdentityLookup},
     Perbill,
 };
-use srml_support::{impl_outer_event, impl_outer_origin, parameter_types};
+use srml_support::{
+    impl_outer_event, impl_outer_origin, parameter_types, StorageLinkedMap, StorageMap,
+};
+use std::marker::PhantomData;
 
 impl_outer_origin! {
         pub enum Origin for Test {}
@@ -79,7 +82,7 @@ impl minting::Trait for Test {
 impl stake::Trait for Test {
     type Currency = Balances;
     type StakePoolId = StakePoolId;
-    type StakingEventsHandler = ();
+    type StakingEventsHandler = StakingEventsHandler<Test>;
     type StakeId = u64;
     type SlashId = u64;
 }
@@ -124,8 +127,13 @@ impl recurringrewards::Trait for Test {
 pub type Balances = balances::Module<Test>;
 pub type System = system::Module<Test>;
 
+parameter_types! {
+    pub const MaxWorkerNumberLimit: u32 = 3;
+}
+
 impl Trait<TestWorkingGroupInstance> for Test {
     type Event = TestEvent;
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
 }
 
 pub type Membership = membership::members::Module<Test>;
@@ -163,3 +171,47 @@ pub fn build_test_externalities() -> runtime_io::TestExternalities {
 
     t.into()
 }
+
+pub struct StakingEventsHandler<T> {
+    pub marker: PhantomData<T>,
+}
+
+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>::exists(*stake_id) {
+            return remaining_imbalance;
+        }
+
+        let hiring_application_id = hiring::ApplicationIdByStakingId::<T>::get(*stake_id);
+
+        if crate::MemberIdByHiringApplicationId::<T, TestWorkingGroupInstance>::exists(
+            hiring_application_id,
+        ) {
+            return <crate::Module<T, TestWorkingGroupInstance>>::refund_working_group_stake(
+                *stake_id,
+                remaining_imbalance,
+            );
+        }
+
+        remaining_imbalance
+    }
+
+    /// 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
+    }
+}

文件差異過大導致無法顯示
+ 221 - 271
runtime-modules/working-group/src/tests/mod.rs


+ 29 - 35
runtime-modules/working-group/src/types.rs

@@ -6,7 +6,7 @@ use rstd::collections::btree_set::BTreeSet;
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
 
-/// Terms for slashings applied to a given role.
+/// 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 {
@@ -28,7 +28,7 @@ pub enum SlashingTerms {
     Slashable(SlashableTerms),
 }
 
-/// Must be default constructible because it indirectly is a value in a storage map.
+/// 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 {
@@ -69,14 +69,14 @@ pub struct OpeningPolicyCommitment<BlockNumber, Balance> {
     /// When terminating a worker: unstaking period for application stake.
     pub terminate_application_stake_unstaking_period: Option<BlockNumber>,
 
-    /// When terminating a worker: unstaking period for role stake.
-    pub terminate_worker_role_stake_unstaking_period: Option<BlockNumber>,
+    /// When terminating a worke/leadr: unstaking period for role stake.
+    pub terminate_role_stake_unstaking_period: Option<BlockNumber>,
 
-    /// When a worker exists: unstaking period for application stake.
-    pub exit_worker_role_application_stake_unstaking_period: Option<BlockNumber>,
+    /// When a worker/lead exists: unstaking period for application stake.
+    pub exit_role_application_stake_unstaking_period: Option<BlockNumber>,
 
-    /// When a worker exists: unstaking period for role stake.
-    pub exit_worker_role_stake_unstaking_period: Option<BlockNumber>,
+    /// When a worker/lead exists: unstaking period for role stake.
+    pub exit_role_stake_unstaking_period: Option<BlockNumber>,
 }
 
 /// An opening for a worker or lead role.
@@ -84,7 +84,7 @@ pub struct OpeningPolicyCommitment<BlockNumber, Balance> {
 #[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 opening_id: OpeningId,
+    pub hiring_opening_id: OpeningId,
 
     /// Set of identifiers for all worker applications ever added.
     pub applications: BTreeSet<WorkerApplicationId>,
@@ -115,23 +115,12 @@ impl Default for OpeningType {
     }
 }
 
-/// Working group lead: worker lead.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct Lead<MemberId, AccountId> {
-    /// Member id of the leader.
-    pub member_id: MemberId,
-
-    /// Account used to authenticate in this role.
-    pub role_account_id: AccountId,
-}
-
-/// An application for the worker role.
+/// An application for the worker/lead role.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
 pub struct Application<AccountId, OpeningId, MemberId, ApplicationId> {
     /// Account used to authenticate in this role.
-    pub role_account: AccountId,
+    pub role_account_id: AccountId,
 
     /// Opening on which this application applies.
     pub opening_id: OpeningId,
@@ -148,13 +137,13 @@ impl<AccountId: Clone, OpeningId: Clone, MemberId: Clone, ApplicationId: Clone>
 {
     /// Creates a new worker application using parameters.
     pub fn new(
-        role_account: &AccountId,
+        role_account_id: &AccountId,
         opening_id: &OpeningId,
         member_id: &MemberId,
         application_id: &ApplicationId,
     ) -> Self {
         Application {
-            role_account: role_account.clone(),
+            role_account_id: role_account_id.clone(),
             opening_id: opening_id.clone(),
             member_id: member_id.clone(),
             hiring_application_id: application_id.clone(),
@@ -162,7 +151,7 @@ impl<AccountId: Clone, OpeningId: Clone, MemberId: Clone, ApplicationId: Clone>
     }
 }
 
-/// Role stake information for a worker/ledd.
+/// Role stake information for a worker/lead.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
 pub struct RoleStakeProfile<StakeId, BlockNumber> {
@@ -177,7 +166,7 @@ pub struct RoleStakeProfile<StakeId, BlockNumber> {
 }
 
 impl<StakeId: Clone, BlockNumber: Clone> RoleStakeProfile<StakeId, BlockNumber> {
-    /// Creates a new worker role stake profile using stake parameters.
+    /// Creates a new worker/lead role stake profile using stake parameters.
     pub fn new(
         stake_id: &StakeId,
         termination_unstaking_period: &Option<BlockNumber>,
@@ -196,13 +185,16 @@ impl<StakeId: Clone, BlockNumber: Clone> RoleStakeProfile<StakeId, BlockNumber>
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
 pub struct Worker<AccountId, RewardRelationshipId, StakeId, BlockNumber, MemberId> {
-    /// Member id related to the worker
+    /// Member id related to the worker/lead.
     pub member_id: MemberId,
+
     /// Account used to authenticate in this role.
-    pub role_account: AccountId,
+    pub role_account_id: AccountId,
+
     /// Whether the role has recurring reward, and if so an identifier for this.
     pub reward_relationship: Option<RewardRelationshipId>,
-    /// When set, describes role stake of worker.
+
+    /// When set, describes role stake of the worker/lead.
     pub role_stake_profile: Option<RoleStakeProfile<StakeId, BlockNumber>>,
 }
 
@@ -217,29 +209,31 @@ impl<
     /// Creates a new _Worker_ using parameters.
     pub fn new(
         member_id: &MemberId,
-        role_account: &AccountId,
+        role_account_id: &AccountId,
         reward_relationship: &Option<RewardRelationshipId>,
         role_stake_profile: &Option<RoleStakeProfile<StakeId, BlockNumber>>,
     ) -> Self {
         Worker {
             member_id: member_id.clone(),
-            role_account: role_account.clone(),
+            role_account_id: role_account_id.clone(),
             reward_relationship: reward_relationship.clone(),
             role_stake_profile: role_stake_profile.clone(),
         }
     }
 }
 
-/// Origin of exit initiation on behalf of a curator.'
+/// Origin of exit initiation.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Debug, Clone, PartialEq)]
 pub enum ExitInitiationOrigin {
-    /// Lead is origin.
+    /// Lead fires the worker.
     Lead,
 
-    /// The curator exiting is the origin.
+    /// Worker leaves the position.
     Worker,
-    //TODO
+
+    /// Council fires the leader.
+    Sudo,
 }
 
 /// The recurring reward if any to be assigned to an actor when filling in the position.

+ 1 - 2
runtime/Cargo.toml

@@ -1,11 +1,10 @@
-
 [package]
 authors = ['Joystream contributors']
 edition = '2018'
 name = 'joystream-node-runtime'
 # Follow convention: https://github.com/Joystream/substrate-runtime-joystream/issues/1
 # {Authoring}.{Spec}.{Impl} of the RuntimeVersion
-version = '6.16.0'
+version = '6.17.0'
 
 [features]
 default = ['std']

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

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

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

@@ -1,2 +1,4 @@
+pub mod content_working_group;
 pub mod proposals;
 pub mod storage;
+pub mod working_group;

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

@@ -2,7 +2,7 @@
 
 use rstd::marker::PhantomData;
 
-use common::origin_validator::ActorOriginValidator;
+use common::origin::ActorOriginValidator;
 use proposals_engine::VotersParameters;
 
 use super::{MemberId, MembershipOriginValidator};
@@ -44,7 +44,7 @@ impl<T: governance::council::Trait> VotersParameters for CouncilManager<T> {
 mod tests {
     use super::CouncilManager;
     use crate::Runtime;
-    use common::origin_validator::ActorOriginValidator;
+    use common::origin::ActorOriginValidator;
     use membership::members::UserInfo;
     use proposals_engine::VotersParameters;
     use sr_primitives::AccountId32;

+ 2 - 2
runtime/src/integration/proposals/membership_origin_validator.rs

@@ -2,7 +2,7 @@
 
 use rstd::marker::PhantomData;
 
-use common::origin_validator::ActorOriginValidator;
+use common::origin::ActorOriginValidator;
 use system::ensure_signed;
 
 /// Member of the Joystream organization
@@ -46,7 +46,7 @@ impl<T: crate::members::Trait>
 mod tests {
     use super::MembershipOriginValidator;
     use crate::Runtime;
-    use common::origin_validator::ActorOriginValidator;
+    use common::origin::ActorOriginValidator;
     use membership::members::UserInfo;
     use sr_primitives::AccountId32;
     use system::RawOrigin;

+ 1 - 1
runtime/src/integration/storage.rs

@@ -8,7 +8,7 @@ pub struct StorageProviderHelper;
 
 impl storage::data_directory::StorageProviderHelper<Runtime> for StorageProviderHelper {
     fn get_random_storage_provider() -> Result<ActorId, &'static str> {
-        let ids = crate::StorageWorkingGroup::get_all_worker_ids();
+        let ids = crate::StorageWorkingGroup::get_regular_worker_ids();
 
         let live_ids: Vec<ActorId> = ids
             .into_iter()

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

@@ -0,0 +1,49 @@
+use rstd::marker::PhantomData;
+use srml_support::{StorageLinkedMap, StorageMap};
+
+use crate::StorageWorkingGroupInstance;
+use stake::{BalanceOf, NegativeImbalance};
+
+pub struct StakingEventsHandler<T> {
+    pub marker: PhantomData<T>,
+}
+
+impl<T: stake::Trait + working_group::Trait<StorageWorkingGroupInstance>>
+    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>::exists(*stake_id) {
+            return remaining_imbalance;
+        }
+
+        let hiring_application_id = hiring::ApplicationIdByStakingId::<T>::get(*stake_id);
+
+        if working_group::MemberIdByHiringApplicationId::<T, StorageWorkingGroupInstance>::exists(
+            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
+    }
+}

+ 17 - 139
runtime/src/lib.rs

@@ -161,7 +161,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
     spec_name: create_runtime_str!("joystream-node"),
     impl_name: create_runtime_str!("joystream-node"),
     authoring_version: 6,
-    spec_version: 16,
+    spec_version: 17,
     impl_version: 0,
     apis: RUNTIME_API_VERSIONS,
 };
@@ -319,7 +319,7 @@ impl transaction_payment::Trait for Runtime {
     type TransactionBaseFee = TransactionBaseFee;
     type TransactionByteFee = TransactionByteFee;
     type WeightToFee = ();
-    type FeeMultiplierUpdate = (); // FeeMultiplierUpdateHandler;
+    type FeeMultiplierUpdate = ();
 }
 
 impl sudo::Trait for Runtime {
@@ -447,7 +447,10 @@ impl versioned_store::Trait for Runtime {
 
 impl versioned_store_permissions::Trait for Runtime {
     type Credential = Credential;
-    type CredentialChecker = (ContentWorkingGroupCredentials, SudoKeyHasAllCredentials);
+    type CredentialChecker = (
+        integration::content_working_group::ContentWorkingGroupCredentials,
+        SudoKeyHasAllCredentials,
+    );
     type CreateClassPermissionsChecker = ContentLeadOrSudoKeyCanCreateClasses;
 }
 
@@ -462,72 +465,6 @@ impl versioned_store_permissions::CredentialChecker<Runtime> for SudoKeyHasAllCr
     }
 }
 
-parameter_types! {
-    pub const CurrentLeadCredential: Credential = 0;
-    pub const AnyActiveCuratorCredential: Credential = 1;
-    pub const AnyActiveChannelOwnerCredential: Credential = 2;
-    pub const PrincipalIdMappingStartsAtCredential: Credential = 1000;
-}
-
-pub struct ContentWorkingGroupCredentials {}
-impl versioned_store_permissions::CredentialChecker<Runtime> for ContentWorkingGroupCredentials {
-    fn account_has_credential(
-        account: &AccountId,
-        credential: <Runtime as versioned_store_permissions::Trait>::Credential,
-    ) -> bool {
-        match credential {
-            // Credentials from 0..999 represents groups or more complex requirements
-            // Current Lead if set
-            credential if credential == CurrentLeadCredential::get() => {
-                match <content_wg::Module<Runtime>>::ensure_lead_is_set() {
-                    Ok((_, lead)) => lead.role_account == *account,
-                    _ => false,
-                }
-            }
-            // Any Active Curator
-            credential if credential == AnyActiveCuratorCredential::get() => {
-                // Look for a Curator with a matching role account
-                for (_principal_id, principal) in <content_wg::PrincipalById<Runtime>>::enumerate()
-                {
-                    if let content_wg::Principal::Curator(curator_id) = principal {
-                        let curator = <content_wg::CuratorById<Runtime>>::get(curator_id);
-                        if curator.role_account == *account
-                            && curator.stage == content_wg::CuratorRoleStage::Active
-                        {
-                            return true;
-                        }
-                    }
-                }
-
-                false
-            }
-            // Any Active Channel Owner
-            credential if credential == AnyActiveChannelOwnerCredential::get() => {
-                // Look for a ChannelOwner with a matching role account
-                for (_principal_id, principal) in <content_wg::PrincipalById<Runtime>>::enumerate()
-                {
-                    if let content_wg::Principal::ChannelOwner(channel_id) = principal {
-                        let channel = <content_wg::ChannelById<Runtime>>::get(channel_id);
-                        if channel.role_account == *account {
-                            return true; // should we also take publishing_status/curation_status into account ?
-                        }
-                    }
-                }
-
-                false
-            }
-            // mapping to workging group principal id
-            n if n >= PrincipalIdMappingStartsAtCredential::get() => {
-                <content_wg::Module<Runtime>>::account_has_credential(
-                    account,
-                    n - PrincipalIdMappingStartsAtCredential::get(),
-                )
-            }
-            _ => false,
-        }
-    }
-}
-
 // Allow sudo key holder permission to create classes
 pub struct SudoKeyCanCreateClasses {}
 impl versioned_store_permissions::CreateClassPermissionsChecker<Runtime>
@@ -591,80 +528,16 @@ impl stake::Trait for Runtime {
     type Currency = <Self as common::currency::GovernanceCurrency>::Currency;
     type StakePoolId = StakePoolId;
     type StakingEventsHandler = (
-        ContentWorkingGroupStakingEventHandler,
-        crate::integration::proposals::StakingEventsHandler<Self>,
+        crate::integration::content_working_group::ContentWorkingGroupStakingEventHandler,
+        (
+            crate::integration::proposals::StakingEventsHandler<Self>,
+            crate::integration::working_group::StakingEventsHandler<Self>,
+        ),
     );
     type StakeId = u64;
     type SlashId = u64;
 }
 
-pub struct ContentWorkingGroupStakingEventHandler {}
-impl stake::StakingEventsHandler<Runtime> for ContentWorkingGroupStakingEventHandler {
-    fn unstaked(
-        stake_id: &<Runtime as stake::Trait>::StakeId,
-        _unstaked_amount: stake::BalanceOf<Runtime>,
-        remaining_imbalance: stake::NegativeImbalance<Runtime>,
-    ) -> stake::NegativeImbalance<Runtime> {
-        if !hiring::ApplicationIdByStakingId::<Runtime>::exists(stake_id) {
-            // Stake not related to a staked role managed by the hiring module
-            return remaining_imbalance;
-        }
-
-        let application_id = hiring::ApplicationIdByStakingId::<Runtime>::get(stake_id);
-
-        if !content_wg::CuratorApplicationById::<Runtime>::exists(application_id) {
-            // Stake not for a Curator
-            return remaining_imbalance;
-        }
-
-        // Notify the Hiring module - is there a potential re-entrancy bug if
-        // instant unstaking is occuring?
-        hiring::Module::<Runtime>::unstaked(*stake_id);
-
-        // Only notify working group module if non instantaneous unstaking occured
-        if content_wg::UnstakerByStakeId::<Runtime>::exists(stake_id) {
-            content_wg::Module::<Runtime>::unstaked(*stake_id);
-        }
-
-        // Determine member id of the curator
-        let curator_application =
-            content_wg::CuratorApplicationById::<Runtime>::get(application_id);
-        let member_id = curator_application.member_id;
-
-        // get member's profile
-        let member_profile = membership::members::MemberProfile::<Runtime>::get(member_id).unwrap();
-
-        // deposit funds to member's root_account
-        // The application doesn't recorded the original source_account from which staked funds were
-        // provided, so we don't really have another option at the moment.
-        <Runtime as stake::Trait>::Currency::resolve_creating(
-            &member_profile.root_account,
-            remaining_imbalance,
-        );
-
-        stake::NegativeImbalance::<Runtime>::zero()
-    }
-
-    // Handler for slashing event
-    fn slashed(
-        _id: &<Runtime as stake::Trait>::StakeId,
-        _slash_id: Option<<Runtime as stake::Trait>::SlashId>,
-        _slashed_amount: stake::BalanceOf<Runtime>,
-        _remaining_stake: stake::BalanceOf<Runtime>,
-        remaining_imbalance: stake::NegativeImbalance<Runtime>,
-    ) -> stake::NegativeImbalance<Runtime> {
-        // Check if the stake is associated with a hired curator or applicant
-        // if their stake goes below minimum required for the role,
-        // they should get deactivated.
-        // Since we don't currently implement any slash initiation in working group,
-        // there is nothing to do for now.
-
-        // Not interested in transfering the slashed amount anywhere for now,
-        // so return it to next handler.
-        remaining_imbalance
-    }
-}
-
 impl content_wg::Trait for Runtime {
     type Event = Event;
 }
@@ -720,7 +593,7 @@ impl members::Trait for Runtime {
  *
  * ForumUserRegistry could have been implemented directly on
  * the membership module, and likewise ForumUser on Profile,
- * however this approach is more loosley coupled.
+ * however this approach is more loosely coupled.
  *
  * Further exploration required to decide what the long
  * run convention should be.
@@ -758,8 +631,13 @@ impl migration::Trait for Runtime {
 // The storage working group instance alias.
 pub type StorageWorkingGroupInstance = working_group::Instance2;
 
+parameter_types! {
+    pub const MaxWorkerNumberLimit: u32 = 100;
+}
+
 impl working_group::Trait<StorageWorkingGroupInstance> for Runtime {
     type Event = Event;
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
 }
 
 impl service_discovery::Trait for Runtime {

部分文件因文件數量過多而無法顯示