Browse Source

Merge branch 'sumer' into sumer-colossus-update

Mokhtar Naamani 4 years ago
parent
commit
08a6023c7e

+ 7 - 0
node/src/chain_spec/mod.rs

@@ -225,6 +225,9 @@ pub fn testnet_genesis(
 
     let default_text_constraint = node_runtime::working_group::default_text_constraint();
 
+    let default_storage_size_constraint =
+        node_runtime::working_group::default_storage_size_constraint();
+
     GenesisConfig {
         system: Some(SystemConfig {
             code: WASM_BINARY.to_vec(),
@@ -311,6 +314,7 @@ pub fn testnet_genesis(
             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,
+            worker_storage_size_constraint: default_storage_size_constraint,
         }),
         working_group_Instance3: Some(ContentDirectoryWorkingGroupConfig {
             phantom: Default::default(),
@@ -318,6 +322,7 @@ pub fn testnet_genesis(
             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,
+            worker_storage_size_constraint: default_storage_size_constraint,
         }),
         working_group_Instance4: Some(BuilderWorkingGroupConfig {
             phantom: Default::default(),
@@ -325,6 +330,7 @@ pub fn testnet_genesis(
             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,
+            worker_storage_size_constraint: default_storage_size_constraint,
         }),
         working_group_Instance5: Some(GatewayWorkingGroupConfig {
             phantom: Default::default(),
@@ -332,6 +338,7 @@ pub fn testnet_genesis(
             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,
+            worker_storage_size_constraint: default_storage_size_constraint,
         }),
         content: Some({
             ContentConfig {

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

@@ -84,6 +84,9 @@ decl_error! {
         /// Worker exit rationale text is too short.
         WorkerExitRationaleTextTooShort,
 
+        /// Worker storage text is too long.
+        WorkerStorageValueTooLong,
+
         /// Signer is not worker role account.
         SignerIsNotWorkerRoleAccount,
 

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

@@ -209,6 +209,12 @@ decl_event!(
         /// - Role account id of the worker.
         WorkerRoleAccountUpdated(WorkerId, AccountId),
 
+        /// Emits on updating the worker storage role.
+        /// Params:
+        /// - Id of the worker.
+        /// - Raw storage field.
+        WorkerStorageUpdated(WorkerId, Vec<u8>),
+
         /// Emits on updating the reward account of the worker.
         /// Params:
         /// - Member id of the worker.
@@ -312,6 +318,10 @@ decl_storage! {
         pub WorkerById get(fn worker_by_id) : map hasher(blake2_128_concat)
             WorkerId<T> => WorkerOf<T>;
 
+        /// Maps identifier to corresponding worker storage.
+        pub WorkerStorage get(fn worker_storage): map hasher(blake2_128_concat)
+            WorkerId<T> => Vec<u8>;
+
         /// Count of active workers.
         pub ActiveWorkerCount get(fn active_worker_count): u32;
 
@@ -321,6 +331,9 @@ decl_storage! {
         /// Worker exit rationale text length limits.
         pub WorkerExitRationaleText get(fn worker_exit_rationale_text) : InputValidationLengthConstraint;
 
+        /// Worker storage size upper bound.
+        pub WorkerStorageSize get(fn worker_storage_size) : u16;
+
         /// Map member id by hiring application id.
         /// Required by StakingEventsHandler callback call to refund the balance on unstaking.
         pub MemberIdByHiringApplicationId get(fn member_id_by_hiring_application_id):
@@ -332,11 +345,13 @@ decl_storage! {
         config(opening_human_readable_text_constraint): InputValidationLengthConstraint;
         config(worker_application_human_readable_text_constraint): InputValidationLengthConstraint;
         config(worker_exit_rationale_text_constraint): InputValidationLengthConstraint;
+        config(worker_storage_size_constraint): u16;
         build(|config: &GenesisConfig<T, I>| {
             Module::<T, I>::initialize_working_group(
                 config.opening_human_readable_text_constraint,
                 config.worker_application_human_readable_text_constraint,
                 config.worker_exit_rationale_text_constraint,
+                config.worker_storage_size_constraint,
                 config.working_group_mint_capacity)
         });
     }
@@ -384,6 +399,30 @@ decl_module! {
             Self::deposit_event(RawEvent::WorkerRoleAccountUpdated(worker_id, new_role_account_id));
         }
 
+        /// Update the associated role storage.
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn update_role_storage(
+            origin,
+            worker_id: WorkerId<T>,
+            storage: Vec<u8>
+        ) {
+
+            // Ensure there is a signer which matches role account of worker corresponding to provided id.
+            Self::ensure_worker_signed(origin, &worker_id)?;
+
+            Self::ensure_worker_role_storage_text_is_valid(&storage)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Complete the role storage update
+            WorkerStorage::<T, I>::insert(worker_id, storage.clone());
+
+            // Trigger event
+            Self::deposit_event(RawEvent::WorkerStorageUpdated(worker_id, storage));
+        }
+
         /// Update the reward account associated with a set reward relationship for the active worker.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn update_reward_account(
@@ -1329,6 +1368,14 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
             )
             .map_err(|e| DispatchError::Other(e))
     }
+
+    fn ensure_worker_role_storage_text_is_valid(text: &[u8]) -> DispatchResult {
+        ensure!(
+            text.len() as u16 <= Self::worker_storage_size(),
+            Error::<T, I>::WorkerStorageValueTooLong
+        );
+        Ok(())
+    }
 }
 
 /// Creates default text constraint.
@@ -1336,6 +1383,11 @@ pub fn default_text_constraint() -> InputValidationLengthConstraint {
     InputValidationLengthConstraint::new(1, 1024)
 }
 
+/// Creates default storage size constraint.
+pub fn default_storage_size_constraint() -> u16 {
+    2048
+}
+
 impl<T: Trait<I>, I: Instance> Module<T, I> {
     /// Callback from StakingEventsHandler. Refunds unstaked imbalance back to the source account.
     pub fn refund_working_group_stake(
@@ -1488,6 +1540,7 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
         opening_human_readable_text_constraint: InputValidationLengthConstraint,
         worker_application_human_readable_text_constraint: InputValidationLengthConstraint,
         worker_exit_rationale_text_constraint: InputValidationLengthConstraint,
+        worker_storage_size_constraint: u16,
         working_group_mint_capacity: minting::BalanceOf<T>,
     ) {
         // Create a mint.
@@ -1505,6 +1558,7 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
             worker_application_human_readable_text_constraint,
         );
         <WorkerExitRationaleText<I>>::put(worker_exit_rationale_text_constraint);
+        <WorkerStorageSize<I>>::put(worker_storage_size_constraint);
     }
 
     // Set worker id as a leader id.

+ 34 - 0
runtime-modules/working-group/src/tests/fixtures.rs

@@ -271,6 +271,40 @@ impl UpdateWorkerRoleAccountFixture {
     }
 }
 
+pub struct UpdateWorkerStorageFixture {
+    worker_id: u64,
+    storage_field: Vec<u8>,
+    origin: RawOrigin<u64>,
+}
+
+impl UpdateWorkerStorageFixture {
+    pub fn default_with_storage_field(worker_id: u64, storage_field: Vec<u8>) -> Self {
+        Self {
+            worker_id,
+            storage_field,
+            origin: RawOrigin::Signed(1),
+        }
+    }
+    pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
+        Self { origin, ..self }
+    }
+
+    pub fn call_and_assert(&self, expected_result: DispatchResult) {
+        let actual_result = TestWorkingGroup::update_role_storage(
+            self.origin.clone().into(),
+            self.worker_id,
+            self.storage_field.clone(),
+        );
+        assert_eq!(actual_result, expected_result);
+
+        if actual_result.is_ok() {
+            let storage = TestWorkingGroup::worker_storage(self.worker_id);
+
+            assert_eq!(storage, self.storage_field);
+        }
+    }
+}
+
 pub fn set_mint_id(mint_id: u64) {
     <crate::Mint<Test, TestWorkingGroupInstance>>::put(mint_id);
 }

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

@@ -169,6 +169,7 @@ pub fn build_test_externalities() -> sp_io::TestExternalities {
             WORKING_GROUP_CONSTRAINT_MIN,
             WORKING_GROUP_CONSTRAINT_DIFF,
         ),
+        worker_storage_size_constraint: crate::default_storage_size_constraint(),
     }
     .assimilate_storage(&mut t)
     .unwrap();

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

@@ -7,6 +7,7 @@ use frame_support::storage::{StorageMap, StorageValue};
 use std::collections::BTreeMap;
 use system::RawOrigin;
 
+use crate::default_storage_size_constraint;
 use crate::tests::hiring_workflow::HiringWorkflow;
 use crate::types::{OpeningPolicyCommitment, OpeningType, RewardPolicy};
 use crate::{Error, RawEvent, Worker};
@@ -1165,6 +1166,109 @@ fn update_worker_role_account_fails_with_invalid_origin() {
     });
 }
 
+#[test]
+fn update_worker_storage_succeeds() {
+    build_test_externalities().execute_with(|| {
+        /*
+           Events are not emitted on block 0.
+           So any dispatchable calls made during genesis block formation will have no events emitted.
+           https://substrate.dev/recipes/2-appetizers/4-events.html
+        */
+        run_to_block(1);
+
+        let storage_field = vec![0u8].repeat(10);
+
+        let worker_id = fill_default_worker_position();
+
+        let update_storage_fixture = UpdateWorkerStorageFixture::default_with_storage_field(
+            worker_id,
+            storage_field.clone(),
+        );
+
+        update_storage_fixture.call_and_assert(Ok(()));
+
+        EventFixture::assert_last_crate_event(RawEvent::WorkerStorageUpdated(
+            worker_id,
+            storage_field,
+        ));
+    });
+}
+
+#[test]
+fn update_worker_storage_by_leader_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let storage_field = vec![0u8].repeat(10);
+
+        let worker_id = HireLeadFixture::default().hire_lead();
+
+        let update_storage_fixture = UpdateWorkerStorageFixture::default_with_storage_field(
+            worker_id,
+            storage_field.clone(),
+        );
+
+        update_storage_fixture.call_and_assert(Ok(()));
+
+        let worker_storage = TestWorkingGroup::worker_storage(worker_id);
+
+        assert_eq!(storage_field, worker_storage);
+    });
+}
+
+#[test]
+fn update_worker_storage_fails_with_invalid_origin_signed_account() {
+    build_test_externalities().execute_with(|| {
+        let worker_id = fill_default_worker_position();
+
+        let storage_field = vec![0u8].repeat(10);
+
+        let update_storage_fixture =
+            UpdateWorkerStorageFixture::default_with_storage_field(worker_id, storage_field)
+                .with_origin(RawOrigin::Signed(2));
+
+        update_storage_fixture.call_and_assert(Err(
+            Error::<Test, TestWorkingGroupInstance>::SignerIsNotWorkerRoleAccount.into(),
+        ));
+    });
+}
+
+#[test]
+fn update_worker_storage_fails_with_invalid_worker_id() {
+    build_test_externalities().execute_with(|| {
+        let storage_field = vec![0u8].repeat(10);
+
+        fill_default_worker_position();
+
+        let invalid_worker_id = 1;
+
+        let update_storage_fixture = UpdateWorkerStorageFixture::default_with_storage_field(
+            invalid_worker_id,
+            storage_field.clone(),
+        );
+
+        update_storage_fixture.call_and_assert(Err(
+            Error::<Test, TestWorkingGroupInstance>::WorkerDoesNotExist.into(),
+        ));
+    });
+}
+
+#[test]
+fn update_worker_storage_fails_with_too_long_text() {
+    build_test_externalities().execute_with(|| {
+        let storage_field = vec![0u8].repeat(default_storage_size_constraint() as usize + 1);
+
+        let worker_id = fill_default_worker_position();
+
+        let update_storage_fixture = UpdateWorkerStorageFixture::default_with_storage_field(
+            worker_id,
+            storage_field.clone(),
+        );
+
+        update_storage_fixture.call_and_assert(Err(
+            Error::<Test, TestWorkingGroupInstance>::WorkerStorageValueTooLong.into(),
+        ));
+    });
+}
+
 #[test]
 fn update_worker_reward_account_succeeds() {
     build_test_externalities().execute_with(|| {

+ 6 - 0
runtime/src/runtime_api.rs

@@ -71,12 +71,17 @@ impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade {
         content::Module::<Runtime>::on_runtime_upgrade();
 
         let default_text_constraint = crate::working_group::default_text_constraint();
+
+        let default_storage_size_constraint =
+            crate::working_group::default_storage_size_constraint();
+
         let default_content_working_group_mint_capacity = 0;
 
         BuilderWorkingGroup::<Runtime>::initialize_working_group(
             default_text_constraint,
             default_text_constraint,
             default_text_constraint,
+            default_storage_size_constraint,
             default_content_working_group_mint_capacity,
         );
 
@@ -84,6 +89,7 @@ impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade {
             default_text_constraint,
             default_text_constraint,
             default_text_constraint,
+            default_storage_size_constraint,
             default_content_working_group_mint_capacity,
         );