Browse Source

runtime: storage: Add extrinsic.

- add ‘create_distribution_bucket’ extrinsic
- add MaxDistributionBucketNumberPerFamily constant
Shamil Gadelshin 3 years ago
parent
commit
e025fa4834

+ 70 - 9
runtime-modules/storage/src/lib.rs

@@ -56,6 +56,8 @@
 //! creates distribution bucket family.
 //! - [delete_distribution_bucket_family](./struct.Module.html#method.delete_distribution_bucket_family) -
 //! deletes distribution bucket family.
+//! - [create_distribution_bucket](./struct.Module.html#method.create_distribution_bucket) -
+//! creates distribution bucket.
 //!
 //! #### Public methods
 //! Public integration methods are exposed via the [DataObjectStorage](./trait.DataObjectStorage.html)
@@ -79,6 +81,7 @@
 //! - DefaultMemberDynamicBagCreationPolicy
 //! - DefaultChannelDynamicBagCreationPolicy
 //! - MaxDistributionBucketFamilyNumber
+//! - MaxDistributionBucketNumberPerFamily
 //!
 
 // Ensure we're `no_std` when compiling for Wasm.
@@ -118,8 +121,6 @@ use bag_manager::BagManager;
 use storage_bucket_picker::StorageBucketPicker;
 
 // TODO: constants
-// Max number of distribution bucket families
-// Max number of distribution buckets per family.
 // Max number of pending invitations per distribution bucket.
 
 /// Public interface for the storage module.
@@ -267,6 +268,9 @@ pub trait Trait: frame_system::Trait + balances::Trait + membership::Trait {
     /// Defines max allowed distribution bucket family number.
     type MaxDistributionBucketFamilyNumber: Get<u64>;
 
+    /// Defines max allowed distribution bucket number per family.
+    type MaxDistributionBucketNumberPerFamily: Get<u64>;
+
     /// Demand the storage working group leader authorization.
     /// TODO: Refactor after merging with the Olympia release.
     fn ensure_storage_working_group_leader_origin(origin: Self::Origin) -> DispatchResult;
@@ -747,12 +751,13 @@ pub struct DistributionBucketFamily<DistributionBucketId: Ord> {
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
 pub struct DistributionBucket {
+    /// Distribution bucket accepts new bags.
+    pub accepting_new_bags: bool,
     //TODO:
-// pending_invitations: BTreeSet<WorkerId>,
-// number_of_pending_data_objects: u32,
-// accepting_new_bags: boolean,
-// distributing: boolean,
-// number_of_operators: u32,
+    // pending_invitations: BTreeSet<WorkerId>,
+    // number_of_pending_data_objects: u32,
+    // distributing: boolean,
+    // number_of_operators: u32,
 }
 
 decl_storage! {
@@ -814,6 +819,9 @@ decl_storage! {
 
         /// Total number of distribution bucket families in the system.
         pub DistributionBucketFamilyNumber get(fn distribution_bucket_family_number): u64;
+
+        /// Distribution bucket id counter. Starts at zero.
+        pub NextDistributionBucketId get(fn next_distribution_bucket_id): T::DistributionBucketId;
     }
 }
 
@@ -830,6 +838,7 @@ decl_event! {
         <T as frame_system::Trait>::AccountId,
         Balance = BalanceOf<T>,
         <T as Trait>::DistributionBucketFamilyId,
+        <T as Trait>::DistributionBucketId,
     {
         /// Emits on creating the storage bucket.
         /// Params
@@ -989,6 +998,13 @@ decl_event! {
         /// Params
         /// - distribution family bucket ID
         DistributionBucketFamilyDeleted(DistributionBucketFamilyId),
+
+        /// Emits on creating distribution bucket.
+        /// Params
+        /// - distribution bucket family ID
+        /// - accepting new bags
+        /// - distribution bucket ID
+        DistributionBucketCreated(DistributionBucketFamilyId, bool, DistributionBucketId),
     }
 }
 
@@ -1111,6 +1127,9 @@ decl_error! {
 
         /// Distribution bucket family doesn't exist.
         DistributionBucketFamilyDoesntExist,
+
+        /// Max distribution bucket number per family limit exceeded.
+        MaxDistributionBucketNumberPerFamilyLimitExceeded,
     }
 }
 
@@ -1150,6 +1169,10 @@ decl_module! {
         /// Exports const - max allowed distribution bucket family number.
         const MaxDistributionBucketFamilyNumber: u64 = T::MaxDistributionBucketFamilyNumber::get();
 
+        /// Exports const - max allowed distribution bucket number per family.
+        const MaxDistributionBucketNumberPerFamily: u64 =
+            T::MaxDistributionBucketNumberPerFamily::get();
+
         // ===== Storage Lead actions =====
 
         /// Delete storage bucket. Must be empty. Storage operator must be missing.
@@ -1677,7 +1700,7 @@ decl_module! {
 
         // ===== Distribution Lead actions =====
 
-        /// Create a distribution family.
+        /// Create a distribution bucket family.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn create_distribution_bucket_family(origin) {
             T::ensure_distribution_working_group_leader_origin(origin)?;
@@ -1707,7 +1730,7 @@ decl_module! {
             Self::deposit_event(RawEvent::DistributionBucketFamilyCreated(family_id));
         }
 
-        /// Deletes a distribution family.
+        /// Deletes a distribution bucket family.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn delete_distribution_bucket_family(origin, family_id: T::DistributionBucketFamilyId) {
             T::ensure_distribution_working_group_leader_origin(origin)?;
@@ -1726,6 +1749,44 @@ decl_module! {
 
             Self::deposit_event(RawEvent::DistributionBucketFamilyDeleted(family_id));
         }
+
+        /// Create a distribution bucket.
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn create_distribution_bucket(
+            origin,
+            family_id: T::DistributionBucketFamilyId,
+            accepting_new_bags: bool,
+        ) {
+            T::ensure_distribution_working_group_leader_origin(origin)?;
+
+            let mut family = Self::ensure_distribution_bucket_family_exists(&family_id)?;
+
+            ensure!(
+                family.distribution_buckets.len().saturated_into::<u64>() <
+                    T::MaxDistributionBucketNumberPerFamily::get(),
+                Error::<T>::MaxDistributionBucketNumberPerFamilyLimitExceeded
+            );
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            let bucket = DistributionBucket {
+                accepting_new_bags
+            };
+
+            let bucket_id = Self::next_distribution_bucket_id();
+
+            family.distribution_buckets.insert(bucket_id, bucket);
+
+            <NextDistributionBucketId<T>>::put(bucket_id + One::one());
+
+            <DistributionBucketFamilyById<T>>::insert(family_id, family);
+
+            Self::deposit_event(
+                RawEvent::DistributionBucketCreated(family_id, accepting_new_bags, bucket_id)
+            );
+        }
     }
 }
 

+ 70 - 6
runtime-modules/storage/src/tests/fixtures.rs

@@ -10,8 +10,9 @@ use super::mocks::{
 };
 
 use crate::{
-    BagId, ContentId, DataObjectCreationParameters, DataObjectStorage, DynamicBagId,
-    DynamicBagType, RawEvent, StaticBagId, StorageBucketOperatorStatus, UploadParameters,
+    BagId, ContentId, DataObjectCreationParameters, DataObjectStorage, DistributionBucketFamily,
+    DynamicBagId, DynamicBagType, RawEvent, StaticBagId, StorageBucketOperatorStatus,
+    UploadParameters,
 };
 
 // Recommendation from Parity on testing on_finalize
@@ -45,6 +46,7 @@ impl EventFixture {
             u64,
             u64,
             u64,
+            u64,
         >,
     ) {
         let converted_event = TestEvent::storage(expected_raw_event);
@@ -63,6 +65,7 @@ impl EventFixture {
             u64,
             u64,
             u64,
+            u64,
         >,
     ) {
         let converted_event = TestEvent::storage(expected_raw_event);
@@ -1178,11 +1181,11 @@ impl UpdateNumberOfStorageBucketsInDynamicBagCreationPolicyFixture {
     }
 }
 
-pub struct CreateDistributionBucketBucketFamilyFixture {
+pub struct CreateDistributionBucketFamilyFixture {
     origin: RawOrigin<u64>,
 }
 
-impl CreateDistributionBucketBucketFamilyFixture {
+impl CreateDistributionBucketFamilyFixture {
     pub fn default() -> Self {
         Self {
             origin: RawOrigin::Signed(DEFAULT_ACCOUNT_ID),
@@ -1229,12 +1232,12 @@ impl CreateDistributionBucketBucketFamilyFixture {
     }
 }
 
-pub struct DeleteDistributionBucketBucketFamilyFixture {
+pub struct DeleteDistributionBucketFamilyFixture {
     origin: RawOrigin<u64>,
     family_id: u64,
 }
 
-impl DeleteDistributionBucketBucketFamilyFixture {
+impl DeleteDistributionBucketFamilyFixture {
     pub fn default(family_id: u64) -> Self {
         Self {
             origin: RawOrigin::Signed(DEFAULT_ACCOUNT_ID),
@@ -1266,3 +1269,64 @@ impl DeleteDistributionBucketBucketFamilyFixture {
         }
     }
 }
+
+pub struct CreateDistributionBucketFixture {
+    origin: RawOrigin<u64>,
+    family_id: u64,
+    accept_new_bags: bool,
+}
+
+impl CreateDistributionBucketFixture {
+    pub fn default(family_id: u64) -> Self {
+        Self {
+            origin: RawOrigin::Signed(DEFAULT_ACCOUNT_ID),
+            family_id,
+            accept_new_bags: false,
+        }
+    }
+
+    pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
+        Self { origin, ..self }
+    }
+
+    pub fn with_accept_new_bags(self, accept_new_bags: bool) -> Self {
+        Self {
+            accept_new_bags,
+            ..self
+        }
+    }
+
+    pub fn call_and_assert(&self, expected_result: DispatchResult) -> Option<u64> {
+        let next_bucket_id = Storage::next_distribution_bucket_id();
+        let actual_result = Storage::create_distribution_bucket(
+            self.origin.clone().into(),
+            self.family_id,
+            self.accept_new_bags,
+        );
+
+        assert_eq!(actual_result, expected_result);
+
+        if actual_result.is_ok() {
+            assert_eq!(next_bucket_id + 1, Storage::next_distribution_bucket_id());
+
+            let family: DistributionBucketFamily<u64> =
+                Storage::distribution_bucket_family_by_id(self.family_id);
+
+            assert!(family.distribution_buckets.contains_key(&next_bucket_id));
+            assert_eq!(
+                family
+                    .distribution_buckets
+                    .get(&next_bucket_id)
+                    .unwrap()
+                    .accepting_new_bags,
+                self.accept_new_bags
+            );
+
+            Some(next_bucket_id)
+        } else {
+            assert_eq!(next_bucket_id, Storage::next_distribution_bucket_id());
+
+            None
+        }
+    }
+}

+ 2 - 0
runtime-modules/storage/src/tests/mocks.rs

@@ -55,6 +55,7 @@ parameter_types! {
     pub const MaxStorageBucketNumber: u64 = 1000;
     pub const MaxNumberOfDataObjectsPerBag: u64 = 4;
     pub const MaxDistributionBucketFamilyNumber: u64 = 1;
+    pub const MaxDistributionBucketNumberPerFamily: u64 = 1;
     pub const DataObjectDeletionPrize: u64 = 10;
     pub const StorageModuleId: ModuleId = ModuleId(*b"mstorage"); // module storage
     pub const BlacklistSizeLimit: u64 = 1;
@@ -96,6 +97,7 @@ impl crate::Trait for Test {
     type Randomness = CollectiveFlip;
     type MaxRandomIterationNumber = MaxRandomIterationNumber;
     type MaxDistributionBucketFamilyNumber = MaxDistributionBucketFamilyNumber;
+    type MaxDistributionBucketNumberPerFamily = MaxDistributionBucketNumberPerFamily;
 
     fn ensure_storage_working_group_leader_origin(origin: Self::Origin) -> DispatchResult {
         let account_id = ensure_signed(origin)?;

+ 77 - 8
runtime-modules/storage/src/tests/mod.rs

@@ -3492,7 +3492,7 @@ fn create_distribution_bucket_family_succeeded() {
         let starting_block = 1;
         run_to_block(starting_block);
 
-        let family_id = CreateDistributionBucketBucketFamilyFixture::default()
+        let family_id = CreateDistributionBucketFamilyFixture::default()
             .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
             .call_and_assert(Ok(()))
             .unwrap();
@@ -3508,7 +3508,7 @@ fn create_distribution_bucket_family_succeeded() {
 #[test]
 fn create_distribution_bucket_family_fails_with_non_signed_origin() {
     build_test_externalities().execute_with(|| {
-        CreateDistributionBucketBucketFamilyFixture::default()
+        CreateDistributionBucketFamilyFixture::default()
             .with_origin(RawOrigin::None)
             .call_and_assert(Err(DispatchError::BadOrigin));
     });
@@ -3517,11 +3517,11 @@ fn create_distribution_bucket_family_fails_with_non_signed_origin() {
 #[test]
 fn create_distribution_bucket_family_fails_with_exceeding_family_number_limit() {
     build_test_externalities().execute_with(|| {
-        CreateDistributionBucketBucketFamilyFixture::default()
+        CreateDistributionBucketFamilyFixture::default()
             .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
             .call_and_assert(Ok(()));
 
-        CreateDistributionBucketBucketFamilyFixture::default()
+        CreateDistributionBucketFamilyFixture::default()
             .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
             .call_and_assert(Err(
                 Error::<Test>::MaxDistributionBucketFamilyNumberLimitExceeded.into(),
@@ -3535,12 +3535,12 @@ fn delete_distribution_bucket_family_succeeded() {
         let starting_block = 1;
         run_to_block(starting_block);
 
-        let family_id = CreateDistributionBucketBucketFamilyFixture::default()
+        let family_id = CreateDistributionBucketFamilyFixture::default()
             .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
             .call_and_assert(Ok(()))
             .unwrap();
 
-        DeleteDistributionBucketBucketFamilyFixture::default(family_id)
+        DeleteDistributionBucketFamilyFixture::default(family_id)
             .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
             .call_and_assert(Ok(()));
 
@@ -3552,7 +3552,7 @@ fn delete_distribution_bucket_family_succeeded() {
 fn delete_distribution_bucket_family_fails_with_non_signed_origin() {
     build_test_externalities().execute_with(|| {
         let invalid_family_id = 100;
-        DeleteDistributionBucketBucketFamilyFixture::default(invalid_family_id)
+        DeleteDistributionBucketFamilyFixture::default(invalid_family_id)
             .with_origin(RawOrigin::None)
             .call_and_assert(Err(DispatchError::BadOrigin));
     });
@@ -3562,10 +3562,79 @@ fn delete_distribution_bucket_family_fails_with_non_signed_origin() {
 fn delete_distribution_bucket_family_fails_with_non_existing_family() {
     build_test_externalities().execute_with(|| {
         let invalid_family_id = 100;
-        DeleteDistributionBucketBucketFamilyFixture::default(invalid_family_id)
+        DeleteDistributionBucketFamilyFixture::default(invalid_family_id)
             .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
             .call_and_assert(Err(
                 Error::<Test>::DistributionBucketFamilyDoesntExist.into()
             ));
     });
 }
+
+#[test]
+fn create_distribution_bucket_succeeded() {
+    build_test_externalities().execute_with(|| {
+        let starting_block = 1;
+        run_to_block(starting_block);
+
+        let accept_new_bags = false;
+
+        let family_id = CreateDistributionBucketFamilyFixture::default()
+            .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
+            .call_and_assert(Ok(()))
+            .unwrap();
+
+        let bucket_id = CreateDistributionBucketFixture::default(family_id)
+            .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
+            .with_accept_new_bags(accept_new_bags)
+            .call_and_assert(Ok(()))
+            .unwrap();
+
+        EventFixture::assert_last_crate_event(RawEvent::DistributionBucketCreated(
+            family_id,
+            accept_new_bags,
+            bucket_id,
+        ));
+    });
+}
+
+#[test]
+fn create_distribution_bucket_fails_with_non_signed_origin() {
+    build_test_externalities().execute_with(|| {
+        let invalid_family_id = 100;
+        CreateDistributionBucketFixture::default(invalid_family_id)
+            .with_origin(RawOrigin::None)
+            .call_and_assert(Err(DispatchError::BadOrigin));
+    });
+}
+
+#[test]
+fn create_distribution_bucket_fails_with_non_existing_family() {
+    build_test_externalities().execute_with(|| {
+        let invalid_family_id = 100;
+        CreateDistributionBucketFixture::default(invalid_family_id)
+            .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
+            .call_and_assert(Err(
+                Error::<Test>::DistributionBucketFamilyDoesntExist.into()
+            ));
+    });
+}
+
+#[test]
+fn create_distribution_bucket_fails_with_exceeding_max_bucket_number() {
+    build_test_externalities().execute_with(|| {
+        let family_id = CreateDistributionBucketFamilyFixture::default()
+            .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
+            .call_and_assert(Ok(()))
+            .unwrap();
+
+        CreateDistributionBucketFixture::default(family_id)
+            .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
+            .call_and_assert(Ok(()));
+
+        CreateDistributionBucketFixture::default(family_id)
+            .with_origin(RawOrigin::Signed(DISTRIBUTION_WG_LEADER_ACCOUNT_ID))
+            .call_and_assert(Err(
+                Error::<Test>::MaxDistributionBucketNumberPerFamilyLimitExceeded.into(),
+            ));
+    });
+}

+ 2 - 0
runtime/src/lib.rs

@@ -653,6 +653,7 @@ parameter_types! {
 }
 
 parameter_types! {
+    pub const MaxDistributionBucketNumberPerFamily: u64 = 20; //TODO: adjust value
     pub const MaxDistributionBucketFamilyNumber: u64 = 20; //TODO: adjust value
     pub const MaxStorageBucketNumber: u64 = 20; //TODO: adjust value
     pub const MaxNumberOfDataObjectsPerBag: u64 = 1000; //TODO: adjust value
@@ -689,6 +690,7 @@ impl storage::Trait for Runtime {
     type Randomness = RandomnessCollectiveFlip;
     type MaxRandomIterationNumber = MaxRandomIterationNumber;
     type MaxDistributionBucketFamilyNumber = MaxDistributionBucketFamilyNumber;
+    type MaxDistributionBucketNumberPerFamily = MaxDistributionBucketNumberPerFamily;
 
     fn ensure_storage_working_group_leader_origin(origin: Self::Origin) -> DispatchResult {
         StorageWorkingGroup::ensure_origin_is_active_leader(origin)