Browse Source

Storage: introduce remove_content extrinsic

iorveth 4 years ago
parent
commit
9ea16aaa7c
1 changed files with 84 additions and 0 deletions
  1. 84 0
      runtime-modules/storage/src/data_directory.rs

+ 84 - 0
runtime-modules/storage/src/data_directory.rs

@@ -109,6 +109,9 @@ decl_error! {
 
         /// Content uploading blocked.
         ContentUploadingBlocked,
+
+        /// Provided owner should be equal o the data object owner under given content id
+        OwnersAreNotEqual
     }
 }
 
@@ -274,6 +277,7 @@ decl_event! {
         StorageProviderId = StorageProviderId<T>,
         Content = Vec<ContentParameters<ContentId<T>, DataObjectTypeId<T>>>,
         ContentId = ContentId<T>,
+        ContentIds = Vec<ContentId<T>>,
         QuotaLimit = u64,
         UploadingStatus = bool
     {
@@ -283,6 +287,12 @@ decl_event! {
         /// - StorageObjectOwner enum.
         ContentAdded(Content, StorageObjectOwner),
 
+        /// Emits on content removal.
+        /// Params:
+        /// - Content parameters representation.
+        /// - StorageObjectOwner enum.
+        ContentRemoved(ContentIds, StorageObjectOwner),
+
         /// Emits when the storage provider accepts a content.
         /// Params:
         /// - Id of the relationship.
@@ -363,6 +373,30 @@ decl_module! {
             Self::deposit_event(RawEvent::ContentAdded(content, owner));
         }
 
+        /// Remove the content from the system.
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn remove_content(
+            origin,
+            owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
+            content_ids: Vec<ContentId<T>>
+        ) {
+
+            // Ensure given origin can perform operation under specific storage object owner
+            Self::ensure_storage_object_owner_origin(origin, &owner)?;
+
+            // Ensure content under given content ids can be successfully removed
+            let content = Self::ensure_content_can_be_removed(&content_ids, &owner)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Let's remove a content
+            Self::delete_content(&owner, &content_ids, content);
+
+            Self::deposit_event(RawEvent::ContentRemoved(content_ids, owner));
+        }
+
         /// Updates storage object owner quota objects limit. Requires leader privileges.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn update_storage_object_owner_quota_objects_limit(
@@ -526,6 +560,35 @@ impl<T: Trait> Module<T> {
         })
     }
 
+    // Ensure content under given content ids can be successfully removed
+    fn ensure_content_can_be_removed(
+        content_ids: &[T::ContentId],
+        owner: &StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
+    ) -> Result<Vec<DataObject<T>>, Error<T>> {
+        let mut content = Vec::new();
+        for content_id in content_ids {
+            let data_object =
+                Self::data_object_by_content_id(content_id).ok_or(Error::<T>::CidNotFound)?;
+            ensure!(data_object.owner == *owner, Error::<T>::OwnersAreNotEqual);
+            content.push(data_object);
+        }
+
+        Ok(content)
+    }
+
+    fn calculate_content_voucher(content: Vec<DataObject<T>>) -> Voucher {
+        let content_length = content.len() as u64;
+
+        let content_size = content
+            .into_iter()
+            .fold(0, |total_size, content| total_size + content.size);
+
+        Voucher {
+            size: content_size,
+            objects: content_length,
+        }
+    }
+
     // Ensures global quota constraints satisfied.
     fn ensure_global_quota_constraints_satisfied(upload_voucher: Voucher) -> DispatchResult {
         let global_quota_voucher = Self::global_quota().calculate_voucher();
@@ -572,6 +635,27 @@ impl<T: Trait> Module<T> {
         <GlobalQuota>::mutate(|global_quota| global_quota.fill_quota(upload_voucher));
     }
 
+    // Complete content removal
+    fn delete_content(
+        owner: &StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
+        content_ids: &[T::ContentId],
+        content: Vec<DataObject<T>>,
+    ) {
+        let removal_voucher = Self::calculate_content_voucher(content);
+
+        for content_id in content_ids {
+            <DataObjectByContentId<T>>::remove(content_id);
+        }
+
+        // Updade owner quota.
+        <Quotas<T>>::mutate(owner, |owner_quota| {
+            owner_quota.release_quota(removal_voucher)
+        });
+
+        // Update global quota
+        <GlobalQuota>::mutate(|global_quota| global_quota.release_quota(removal_voucher));
+    }
+
     fn ensure_content_is_valid(
         multi_content: &[ContentParameters<T::ContentId, DataObjectTypeId<T>>],
     ) -> DispatchResult {