Bläddra i källkod

Merge branch 'nicaea' into storage-working-group-applying

Leszek Wiesner 4 år sedan
förälder
incheckning
0ca5864a1f

+ 5 - 0
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 {

+ 5 - 0
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 {

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

@@ -244,6 +244,9 @@ decl_error! {
 
         /// Require signed origin in extrinsics.
         RequireSignedOrigin,
+
+        /// Working group size limit exceeded.
+        MaxActiveWorkerNumberExceeded,
     }
 }
 

+ 34 - 2
runtime-modules/working-group/src/lib.rs

@@ -57,8 +57,8 @@ use rstd::collections::btree_set::BTreeSet;
 use rstd::prelude::*;
 use rstd::vec::Vec;
 use sr_primitives::traits::{Bounded, One, Zero};
-use srml_support::traits::{Currency, ExistenceRequirement, Imbalance, WithdrawReasons};
-use srml_support::{decl_event, decl_module, decl_storage, ensure, print};
+use srml_support::traits::{Currency, ExistenceRequirement, Get, Imbalance, WithdrawReasons};
+use srml_support::{decl_event, decl_module, decl_storage, ensure, print, StorageValue};
 use system::{ensure_root, ensure_signed};
 
 use crate::types::ExitInitiationOrigin;
@@ -157,6 +157,9 @@ pub trait Trait<I: Instance>:
 {
     /// _Working group_ event type.
     type Event: From<Event<Self, I>> + Into<<Self as system::Trait>::Event>;
+
+    /// Defines max workers number in the working group.
+    type MaxWorkerNumberLimit: Get<u32>;
 }
 
 decl_event!(
@@ -306,6 +309,9 @@ decl_storage! {
         /// Maps identifier to corresponding worker.
         pub WorkerById get(worker_by_id) : linked_map WorkerId<T> => WorkerOf<T>;
 
+        /// Count of active workers.
+        pub ActiveWorkerCount get(fn active_worker_count): u32;
+
         /// Next identifier for new worker.
         pub NextWorkerId get(next_worker_id) : WorkerId<T>;
 
@@ -342,6 +348,9 @@ decl_module! {
         /// Predefined errors
         type Error = Error;
 
+        /// Exports const -  max simultaneous active worker number.
+        const MaxWorkerNumberLimit: u32 = T::MaxWorkerNumberLimit::get();
+
         // ****************** Roles lifecycle **********************
 
         /// Update the associated role account of the active worker/lead.
@@ -507,6 +516,7 @@ decl_module! {
 
             Self::ensure_opening_human_readable_text_is_valid(&human_readable_text)?;
 
+
             // Add opening
             // NB: This call can in principle fail, because the staking policies
             // may not respect the minimum currency requirement.
@@ -770,6 +780,14 @@ decl_module! {
 
             Self::ensure_origin_for_opening_type(origin, opening.opening_type)?;
 
+            let potential_worker_number =
+                Self::active_worker_count() + (successful_application_ids.len() as u32);
+
+            ensure!(
+                potential_worker_number <= T::MaxWorkerNumberLimit::get(),
+                Error::MaxActiveWorkerNumberExceeded
+            );
+
             // Cannot hire a lead when another leader exists.
             if matches!(opening.opening_type, OpeningType::Leader) {
                 ensure!(!<CurrentLead<T,I>>::exists(), Error::CannotHireLeaderWhenLeaderExists);
@@ -1322,6 +1340,7 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
 
         // Remove the worker from the storage.
         WorkerById::<T, I>::remove(worker_id);
+        Self::decrease_active_worker_counter();
 
         // Trigger the event
         let event = match exit_initiation_origin {
@@ -1456,6 +1475,7 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
 
                 // Store a worker
                 <WorkerById<T, I>>::insert(new_worker_id, worker);
+                Self::increase_active_worker_counter();
 
                 // Update next worker id
                 <NextWorkerId<T, I>>::mutate(|id| *id += <WorkerId<T> as One>::one());
@@ -1470,4 +1490,16 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
 
         application_id_to_worker_id
     }
+
+    // Increases active worker counter (saturating).
+    fn increase_active_worker_counter() {
+        let next_active_worker_count_value = Self::active_worker_count().saturating_add(1);
+        <ActiveWorkerCount<I>>::put(next_active_worker_count_value);
+    }
+
+    // Decreases active worker counter (saturating).
+    fn decrease_active_worker_counter() {
+        let next_active_worker_count_value = Self::active_worker_count().saturating_sub(1);
+        <ActiveWorkerCount<I>>::put(next_active_worker_count_value);
+    }
 }

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

@@ -113,7 +113,7 @@ impl HiringWorkflow {
             SetLeadFixture::default().set_lead();
         }
         increase_total_balance_issuance_using_account_id(1, 10000);
-        setup_members(3);
+        setup_members(4);
         set_mint_id(create_mint());
     }
 

+ 5 - 0
runtime-modules/working-group/src/tests/mock.rs

@@ -127,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>;

+ 73 - 0
runtime-modules/working-group/src/tests/mod.rs

@@ -1872,3 +1872,76 @@ fn ensure_setting_genesis_constraints_succeeds() {
         assert_eq!(worker_exit_text_constraint, default_constraint);
     });
 }
+
+#[test]
+fn active_worker_counter_works_successfully() {
+    build_test_externalities().execute_with(|| {
+        assert_eq!(TestWorkingGroup::active_worker_count(), 0);
+
+        let leader_id = HireLeadFixture::default().hire_lead();
+        assert_eq!(TestWorkingGroup::active_worker_count(), 1);
+
+        let worker_id1 = fill_worker_position(
+            None,
+            None,
+            false,
+            OpeningType::Worker,
+            Some(b"worker1".to_vec()),
+        );
+        assert_eq!(TestWorkingGroup::active_worker_count(), 2);
+
+        let worker_id2 = fill_worker_position(
+            None,
+            None,
+            false,
+            OpeningType::Worker,
+            Some(b"worker1".to_vec()),
+        );
+        assert_eq!(TestWorkingGroup::active_worker_count(), 3);
+
+        TerminateWorkerRoleFixture::default_for_worker_id(worker_id1).call_and_assert(Ok(()));
+        assert_eq!(TestWorkingGroup::active_worker_count(), 2);
+
+        TerminateWorkerRoleFixture::default_for_worker_id(worker_id2).call_and_assert(Ok(()));
+        assert_eq!(TestWorkingGroup::active_worker_count(), 1);
+
+        TerminateWorkerRoleFixture::default_for_worker_id(leader_id)
+            .with_origin(RawOrigin::Root)
+            .call_and_assert(Ok(()));
+        assert_eq!(TestWorkingGroup::active_worker_count(), 0);
+    });
+}
+
+#[test]
+fn adding_too_much_workers_fails_with_single_application_out_of_limit() {
+    build_test_externalities().execute_with(|| {
+        HireLeadFixture::default().hire_lead();
+
+        fill_worker_position(None, None, false, OpeningType::Worker, None);
+        fill_worker_position(None, None, false, OpeningType::Worker, None);
+
+        let hiring_workflow = HiringWorkflow::default()
+            .disable_setup_environment()
+            .add_default_application()
+            .expect(Err(Error::MaxActiveWorkerNumberExceeded));
+
+        hiring_workflow.execute()
+    });
+}
+
+#[test]
+fn fill_opening_cannot_hire_more_workers_using_several_applicationst_han_allows_worker_limit() {
+    build_test_externalities().execute_with(|| {
+        HireLeadFixture::default().hire_lead();
+
+        fill_worker_position(None, None, false, OpeningType::Worker, None);
+
+        let hiring_workflow = HiringWorkflow::default()
+            .disable_setup_environment()
+            .add_application_with_origin(b"Some1".to_vec(), RawOrigin::Signed(2), 2)
+            .add_application_with_origin(b"Some2".to_vec(), RawOrigin::Signed(3), 3)
+            .expect(Err(Error::MaxActiveWorkerNumberExceeded));
+
+        hiring_workflow.execute()
+    });
+}

+ 5 - 0
runtime/src/lib.rs

@@ -631,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 {