Quellcode durchsuchen

content-dir: impl Channels (wip)

Mokhtar Naamani vor 4 Jahren
Ursprung
Commit
a10eddd43d

+ 4 - 2
runtime-modules/common/src/lib.rs

@@ -32,7 +32,8 @@ pub trait MembershipTypes: system::Trait {
         + Copy
         + MaybeSerialize
         + Ord
-        + PartialEq;
+        + PartialEq
+        + Eq;
 
     /// Describes the common type for the working group members (workers).
     type ActorId: Parameter
@@ -43,7 +44,8 @@ pub trait MembershipTypes: system::Trait {
         + Copy
         + MaybeSerialize
         + Ord
-        + PartialEq;
+        + PartialEq
+        + Eq;
 }
 
 /// Generic trait for strorage ownership dependent pallets.

+ 15 - 0
runtime-modules/content/src/errors.rs

@@ -43,5 +43,20 @@ decl_error! {
 
         /// Expected root or signed origin
         BadOrigin,
+
+        /// Operation Cannot be perfomed with this Actor
+        OperationDeniedForActor,
+
+        /// It is not valid to use this variant of a ChannelOwner
+        BadChannelOwner,
+
+        /// Channel Category Does not Exist.
+        ChannelCategoryDoesNotExist,
+
+        /// Channel Does Not Exist
+        ChannelDoesNotExist,
+
+        /// Cannot convert channelowner to an object owner
+        CannotConverChannelOwnerToObjectOwner,
     }
 }

+ 180 - 15
runtime-modules/content/src/lib.rs

@@ -28,9 +28,12 @@ use sp_std::collections::btree_set::BTreeSet;
 use sp_std::vec::Vec;
 use system::ensure_signed;
 
-pub use common::storage::{ContentParameters, StorageSystem};
+pub use common::storage::{
+    AbstractStorageObjectOwner, ContentParameters, StorageObjectOwner, StorageSystem,
+};
 pub use common::{
     currency::{BalanceOf, GovernanceCurrency},
+    working_group::WorkingGroup,
     MembershipTypes, StorageOwnership,
 };
 
@@ -38,6 +41,8 @@ pub(crate) type ContentId<T> = <T as StorageOwnership>::ContentId;
 
 pub(crate) type DataObjectTypeId<T> = <T as StorageOwnership>::DataObjectTypeId;
 
+pub(crate) type UploadParameters<T> = ContentParameters<ContentId<T>, DataObjectTypeId<T>>;
+
 /// Type, used in diffrent numeric constraints representations
 pub type MaxNumber = u32;
 
@@ -111,9 +116,9 @@ pub trait Trait:
 /// Channels, Videos, Series and Person
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
-pub enum NewAsset<ContentParameters> {
+pub enum NewAsset<UploadParameters> {
     /// Upload to the storage system
-    Upload(ContentParameters),
+    Upload(UploadParameters),
     /// A url string pointing at an asset
     Uri(Vec<u8>),
 }
@@ -201,15 +206,20 @@ pub type Channel<T> = ChannelInternal<
 /// A request to buy a channel by a new ChannelOwner.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct ChannelOwnershipTransferRequestType<ChannelId, MemberId, CuratorGroupId, DAOId, Balance>
-{
+pub struct ChannelOwnershipTransferRequestInternal<
+    ChannelId,
+    MemberId,
+    CuratorGroupId,
+    DAOId,
+    Balance,
+> {
     channel_id: ChannelId,
     new_owner: ChannelOwner<MemberId, CuratorGroupId, DAOId>,
     payment: Balance,
 }
 
 // ChannelOwnershipTransferRequest type alias for simplification.
-pub type ChannelOwnershipTransferRequest<T> = ChannelOwnershipTransferRequestType<
+pub type ChannelOwnershipTransferRequest<T> = ChannelOwnershipTransferRequestInternal<
     <T as StorageOwnership>::ChannelId,
     <T as MembershipTypes>::MemberId,
     <T as ContentActorAuthenticator>::CuratorGroupId,
@@ -643,21 +653,131 @@ decl_module! {
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn create_channel(
             origin,
+            actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
             owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
             assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
             params: ChannelCreationParameters<T::ChannelCategoryId>,
         ) -> DispatchResult {
+            let _sender = ensure_signed(origin.clone())?;
+
+            ensure_actor_authorized_to_create_or_update_channel::<T>(
+                origin,
+                &actor,
+                &owner,
+            )?;
+
+            // Ensure the channel category exists
+            Self::ensure_channel_category_exists(&params.in_category)?;
+
+            // Pick out the assets to be uploaded to storage system
+            let upload_parameters: Vec<UploadParameters<T>> = Self::pick_upload_parameters_from_assets(&assets);
+
+            let object_owner = Self::channel_owner_to_object_owner(&owner)?;
+
+            // check assets can be uploaded to storage.
+            // update can_add_content() to only take &refrences
+            T::StorageSystem::can_add_content(
+                object_owner.clone(),
+                upload_parameters.clone(),
+            )?;
+
+            // TODO:
+            // Enough funds to create channel?
+            // Limit on number of channels per owner?
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // TODO: Burn funds from sender account as fee for creating a channel
+
+            // add assets to storage
+            // This should not fail because of prior can_add_content() check!
+            T::StorageSystem::atomically_add_content(
+                object_owner,
+                upload_parameters,
+            )?;
+
+            let channel_id = NextChannelId::<T>::get();
+            NextChannelId::<T>::mutate(|id| *id += T::ChannelId::one());
+
+            let channel: Channel<T> = ChannelInternal {
+                owner: owner.clone(),
+                number_of_videos: 0,
+                number_of_playlists: 0,
+                number_of_series: 0,
+                in_category: params.in_category,
+                is_curated: false,
+                revenue: BalanceOf::<T>::from(0),
+            };
+            ChannelById::<T>::insert(channel_id, channel.clone());
+
+            if let ChannelOwner::CuratorGroup(curator_group_id) = owner {
+                Self::increment_number_of_channels_owned_by_curator_group(curator_group_id);
+            }
+
+            Self::deposit_event(RawEvent::ChannelCreated(channel_id, channel, assets, params));
             Ok(())
         }
 
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn update_channel(
             origin,
-            owner: ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
+            actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
             channel_id: T::ChannelId,
-            new_assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
+            assets: Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>,
             params: ChannelUpdateParameters<T::ChannelCategoryId>,
         ) -> DispatchResult {
+            let _sender = ensure_signed(origin.clone())?;
+
+            // check that channel exists
+            let channel = Self::ensure_channel_exists(&channel_id)?;
+
+            ensure_actor_authorized_to_create_or_update_channel::<T>(
+                origin,
+                &actor,
+                &channel.owner,
+            )?;
+
+            // change of category?
+            if let Some(new_category_id) = params.new_in_category {
+                Self::ensure_channel_category_exists(&new_category_id)?;
+            }
+
+            // Pick out the assets to be uploaded to storage system
+            let upload_parameters: Vec<UploadParameters<T>> = Self::pick_upload_parameters_from_assets(&assets);
+
+            let object_owner = Self::channel_owner_to_object_owner(&channel.owner)?;
+
+            // check assets can be uploaded to storage.
+            // update can_add_content() to only take &refrences
+            T::StorageSystem::can_add_content(
+                object_owner.clone(),
+                upload_parameters.clone(),
+            )?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            let mut channel = channel;
+
+            // change of category, already validated
+            if let Some(new_category_id) = params.new_in_category {
+                channel.in_category = new_category_id;
+            }
+
+            // update the channel
+            ChannelById::<T>::insert(channel_id, channel.clone());
+
+            // add assets to storage
+            // This should not fail because of prior can_add_content() check!
+            T::StorageSystem::atomically_add_content(
+                object_owner,
+                upload_parameters,
+            )?;
+
+            Self::deposit_event(RawEvent::ChannelUpdated(channel_id, channel, assets, params));
             Ok(())
         }
 
@@ -806,6 +926,9 @@ decl_module! {
             curator: T::CuratorId,
             params: ChannelCategoryCreationParameters,
         ) -> DispatchResult {
+            // check origin is curator in active curator group
+            // any curator can create a category
+            // create category and emit event
             Ok(())
         }
 
@@ -971,11 +1094,8 @@ impl<T: Trait> Module<T> {
         });
     }
 
-    // TODO: make this private again after used in module
     /// Increment number of classes, maintained by curator group
-    pub fn increment_number_of_channels_owned_by_curator_group(
-        curator_group_id: T::CuratorGroupId,
-    ) {
+    fn increment_number_of_channels_owned_by_curator_group(curator_group_id: T::CuratorGroupId) {
         <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
             curator_group.increment_number_of_channels_owned_count();
         });
@@ -1020,6 +1140,49 @@ impl<T: Trait> Module<T> {
         }
         Ok(())
     }
+
+    fn ensure_channel_exists(channel_id: &T::ChannelId) -> Result<Channel<T>, Error<T>> {
+        ensure!(
+            ChannelById::<T>::contains_key(channel_id),
+            Error::<T>::ChannelDoesNotExist
+        );
+        Ok(ChannelById::<T>::get(channel_id))
+    }
+
+    fn ensure_channel_category_exists(
+        channel_category_id: &T::ChannelCategoryId
+    ) -> Result<ChannelCategory, Error<T>> {
+        ensure!(
+            ChannelCategoryById::<T>::contains_key(channel_category_id),
+            Error::<T>::ChannelCategoryDoesNotExist
+        );
+        Ok(ChannelCategoryById::<T>::get(channel_category_id))
+    }
+
+    fn pick_upload_parameters_from_assets(
+        assets: &Vec<NewAsset<ContentParameters<T::ContentId, T::DataObjectTypeId>>>
+    ) -> Vec<UploadParameters<T>> {
+        assets.clone().into_iter().filter_map(|asset| {
+            match asset {
+                NewAsset::Upload(upload_parameters) => Some(upload_parameters),
+                _ => None,
+            }
+        }).collect()
+    }
+
+    fn channel_owner_to_object_owner(
+        channel_owner: &ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>
+    ) -> Result<StorageObjectOwner<T::MemberId, T::ChannelId, T::DAOId>, Error::<T>> {
+        match channel_owner {
+            ChannelOwner::Member(member_id) => Ok(StorageObjectOwner::Member(*member_id)),
+            ChannelOwner::CuratorGroup(_id) => {
+                Ok(StorageObjectOwner::AbstractStorageObjectOwner(
+                    AbstractStorageObjectOwner::WorkingGroup(WorkingGroup::Content)
+                ))
+            },
+            _ => Err(Error::<T>::CannotConverChannelOwnerToObjectOwner),
+        }
+    }
 }
 
 // Some initial config for the module on runtime upgrade
@@ -1044,16 +1207,17 @@ decl_event!(
         VideoId = <T as Trait>::VideoId,
         VideoCategoryId = <T as Trait>::VideoCategoryId,
         ChannelId = <T as StorageOwnership>::ChannelId,
-        MemberId = <T as MembershipTypes>::MemberId,
+        // MemberId = <T as MembershipTypes>::MemberId,
         NewAsset = NewAsset<ContentParameters<ContentId<T>, DataObjectTypeId<T>>>,
         ChannelCategoryId = <T as Trait>::ChannelCategoryId,
         ChannelOwnershipTransferRequestId = <T as Trait>::ChannelOwnershipTransferRequestId,
         PlaylistId = <T as Trait>::PlaylistId,
         SeriesId = <T as Trait>::SeriesId,
         PersonId = <T as Trait>::PersonId,
-        DAOId = <T as StorageOwnership>::DAOId,
+        // DAOId = <T as StorageOwnership>::DAOId,
         ChannelOwnershipTransferRequest = ChannelOwnershipTransferRequest<T>,
         Series = Series<<T as StorageOwnership>::ChannelId, <T as Trait>::VideoId>,
+        Channel = Channel<T>,
     {
         // Curators
         CuratorGroupCreated(CuratorGroupId),
@@ -1065,12 +1229,13 @@ decl_event!(
         // Channels
         ChannelCreated(
             ChannelId,
-            ChannelOwner<MemberId, CuratorGroupId, DAOId>,
+            Channel,
             Vec<NewAsset>,
             ChannelCreationParameters<ChannelCategoryId>,
         ),
         ChannelUpdated(
             ChannelId,
+            Channel,
             Vec<NewAsset>,
             ChannelUpdateParameters<ChannelCategoryId>,
         ),

+ 43 - 2
runtime-modules/content/src/permissions/mod.rs

@@ -87,10 +87,51 @@ pub fn ensure_is_lead<T: Trait>(origin: T::Origin) -> DispatchResult {
     Ok(ensure_lead_auth_success::<T>(&account_id)?)
 }
 
+pub fn ensure_actor_authorized_to_create_or_update_channel<T: Trait>(
+    origin: T::Origin,
+    actor: &ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
+    owner: &ChannelOwner<T::MemberId, T::CuratorGroupId, T::DAOId>,
+) -> DispatchResult {
+    let sender = ensure_signed(origin)?;
+
+    // Authenticate actor and proposed channel owner
+    match actor {
+        // Lead should use their member or curator role to create or update channels.
+        ContentActor::Lead => Err(Error::<T>::OperationDeniedForActor),
+        ContentActor::Curator(curator_group_id, curator_id) => {
+            // Authorize curator, performing all checks to ensure curator can act
+            CuratorGroup::<T>::perform_curator_in_group_auth(
+                curator_id,
+                curator_group_id,
+                &sender,
+            )?;
+
+            // Ensure curator group specified as channel owner is same as authenticated group id.
+            ensure!(
+                *owner == ChannelOwner::CuratorGroup(*curator_group_id),
+                Error::<T>::OperationDeniedForActor
+            );
+
+            Ok(())
+        }
+        ContentActor::Member(member_id) => {
+            ensure_member_auth_success::<T>(member_id, &sender)?;
+
+            ensure!(
+                *owner == ChannelOwner::Member(*member_id),
+                Error::<T>::OperationDeniedForActor
+            );
+
+            Ok(())
+        }
+    }?;
+    Ok(())
+}
+
 /// Enum, representing all possible `Actor`s
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Eq, PartialEq, Clone, Copy, Debug)]
-pub enum Actor<
+pub enum ContentActor<
     CuratorGroupId: Default + Clone + Copy,
     CuratorId: Default + Clone + Copy,
     MemberId: Default + Clone + Copy,
@@ -104,7 +145,7 @@ impl<
         CuratorGroupId: Default + Clone + Copy,
         CuratorId: Default + Clone + Copy,
         MemberId: Default + Clone + Copy,
-    > Default for Actor<CuratorGroupId, CuratorId, MemberId>
+    > Default for ContentActor<CuratorGroupId, CuratorId, MemberId>
 {
     fn default() -> Self {
         Self::Lead