data_directory.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. //! # Data directory module
  2. //! Data directory module for the Joystream platform manages IPFS content id, storage providers,
  3. //! owners of the content. It allows to add and accept or reject the content in the system.
  4. //!
  5. //! ## Comments
  6. //!
  7. //! Data object type registry module uses working group module to authorize actions.
  8. //!
  9. //! ## Supported extrinsics
  10. //!
  11. //! ### Public extrinsic
  12. //! - [add_content](./struct.Module.html#method.add_content) - Adds the content to the system.
  13. //!
  14. //! ### Private extrinsics
  15. //! - accept_content - Storage provider accepts a content.
  16. //! - reject_content - Storage provider rejects a content.
  17. //! - remove_known_content_id - Removes the content id from the list of known content ids. Requires root privileges.
  18. //! - set_known_content_id - Sets the content id from the list of known content ids. Requires root privileges.
  19. //!
  20. // Do not delete! Cannot be uncommented by default, because of Parity decl_module! issue.
  21. //#![warn(missing_docs)]
  22. use codec::{Decode, Encode};
  23. use rstd::prelude::*;
  24. use sr_primitives::traits::{MaybeSerialize, Member};
  25. use srml_support::{decl_error, decl_event, decl_module, decl_storage, ensure, Parameter};
  26. use system::{self, ensure_root};
  27. use common::origin::ActorOriginValidator;
  28. pub(crate) use common::BlockAndTime;
  29. use crate::data_object_type_registry;
  30. use crate::data_object_type_registry::IsActiveDataObjectType;
  31. use crate::{MemberId, StorageProviderId, StorageWorkingGroup, StorageWorkingGroupInstance};
  32. /// The _Data directory_ main _Trait_.
  33. pub trait Trait:
  34. timestamp::Trait
  35. + system::Trait
  36. + data_object_type_registry::Trait
  37. + membership::members::Trait
  38. + working_group::Trait<StorageWorkingGroupInstance>
  39. {
  40. /// _Data directory_ event type.
  41. type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
  42. /// Content id.
  43. type ContentId: Parameter + Member + MaybeSerialize + Copy + Ord + Default;
  44. /// Provides random storage provider id.
  45. type StorageProviderHelper: StorageProviderHelper<Self>;
  46. ///Active data object type validator.
  47. type IsActiveDataObjectType: data_object_type_registry::IsActiveDataObjectType<Self>;
  48. /// Validates member id and origin combination.
  49. type MemberOriginValidator: ActorOriginValidator<Self::Origin, MemberId<Self>, Self::AccountId>;
  50. }
  51. decl_error! {
  52. /// _Data object storage registry_ module predefined errors.
  53. pub enum Error {
  54. /// Content with this ID not found.
  55. CidNotFound,
  56. /// Only the liaison for the content may modify its status.
  57. LiaisonRequired,
  58. /// Cannot create content for inactive or missing data object type.
  59. DataObjectTypeMustBeActive,
  60. /// "Data object already added under this content id".
  61. DataObjectAlreadyAdded,
  62. /// Require root origin in extrinsics.
  63. RequireRootOrigin,
  64. }
  65. }
  66. impl From<system::Error> for Error {
  67. fn from(error: system::Error) -> Self {
  68. match error {
  69. system::Error::Other(msg) => Error::Other(msg),
  70. system::Error::RequireRootOrigin => Error::RequireRootOrigin,
  71. _ => Error::Other(error.into()),
  72. }
  73. }
  74. }
  75. impl From<working_group::Error> for Error {
  76. fn from(error: working_group::Error) -> Self {
  77. match error {
  78. working_group::Error::Other(msg) => Error::Other(msg),
  79. _ => Error::Other(error.into()),
  80. }
  81. }
  82. }
  83. /// The decision of the storage provider when it acts as liaison.
  84. #[derive(Clone, Encode, Decode, PartialEq, Debug)]
  85. pub enum LiaisonJudgement {
  86. /// Content awaits for a judgment.
  87. Pending,
  88. /// Content accepted.
  89. Accepted,
  90. /// Content rejected.
  91. Rejected,
  92. }
  93. impl Default for LiaisonJudgement {
  94. fn default() -> Self {
  95. LiaisonJudgement::Pending
  96. }
  97. }
  98. /// Manages content ids, type and storage provider decision about it.
  99. #[derive(Clone, Encode, Decode, PartialEq, Debug)]
  100. pub struct DataObject<T: Trait> {
  101. /// Content owner.
  102. pub owner: MemberId<T>,
  103. /// Content added at.
  104. pub added_at: BlockAndTime<T::BlockNumber, T::Moment>,
  105. /// Content type id.
  106. pub type_id: <T as data_object_type_registry::Trait>::DataObjectTypeId,
  107. /// Content size in bytes.
  108. pub size: u64,
  109. /// Storage provider id of the liaison.
  110. pub liaison: StorageProviderId<T>,
  111. /// Storage provider as liaison judgment.
  112. pub liaison_judgement: LiaisonJudgement,
  113. /// IPFS content id.
  114. pub ipfs_content_id: Vec<u8>,
  115. }
  116. decl_storage! {
  117. trait Store for Module<T: Trait> as DataDirectory {
  118. /// List of ids known to the system.
  119. pub KnownContentIds get(known_content_ids): Vec<T::ContentId> = Vec::new();
  120. /// Maps data objects by their content id.
  121. pub DataObjectByContentId get(data_object_by_content_id):
  122. map T::ContentId => Option<DataObject<T>>;
  123. }
  124. }
  125. decl_event! {
  126. /// _Data directory_ events
  127. pub enum Event<T> where
  128. <T as Trait>::ContentId,
  129. MemberId = MemberId<T>,
  130. StorageProviderId = StorageProviderId<T>
  131. {
  132. /// Emits on adding of the content.
  133. /// Params:
  134. /// - Id of the relationship.
  135. /// - Id of the member.
  136. ContentAdded(ContentId, MemberId),
  137. /// Emits when the storage provider accepts a content.
  138. /// Params:
  139. /// - Id of the relationship.
  140. /// - Id of the storage provider.
  141. ContentAccepted(ContentId, StorageProviderId),
  142. /// Emits when the storage provider rejects a content.
  143. /// Params:
  144. /// - Id of the relationship.
  145. /// - Id of the storage provider.
  146. ContentRejected(ContentId, StorageProviderId),
  147. }
  148. }
  149. decl_module! {
  150. /// _Data directory_ substrate module.
  151. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  152. /// Default deposit_event() handler
  153. fn deposit_event() = default;
  154. /// Predefined errors.
  155. type Error = Error;
  156. /// Adds the content to the system. Member id should match its origin. The created DataObject
  157. /// awaits liaison to accept or reject it.
  158. pub fn add_content(
  159. origin,
  160. member_id: MemberId<T>,
  161. content_id: T::ContentId,
  162. type_id: <T as data_object_type_registry::Trait>::DataObjectTypeId,
  163. size: u64,
  164. ipfs_content_id: Vec<u8>
  165. ) {
  166. T::MemberOriginValidator::ensure_actor_origin(
  167. origin,
  168. member_id,
  169. )?;
  170. ensure!(T::IsActiveDataObjectType::is_active_data_object_type(&type_id),
  171. Error::DataObjectTypeMustBeActive);
  172. ensure!(!<DataObjectByContentId<T>>::exists(content_id),
  173. Error::DataObjectAlreadyAdded);
  174. let liaison = T::StorageProviderHelper::get_random_storage_provider()?;
  175. // Let's create the entry then
  176. let data: DataObject<T> = DataObject {
  177. type_id,
  178. size,
  179. added_at: common::current_block_time::<T>(),
  180. owner: member_id,
  181. liaison,
  182. liaison_judgement: LiaisonJudgement::Pending,
  183. ipfs_content_id,
  184. };
  185. //
  186. // == MUTATION SAFE ==
  187. //
  188. <DataObjectByContentId<T>>::insert(&content_id, data);
  189. Self::deposit_event(RawEvent::ContentAdded(content_id, member_id));
  190. }
  191. /// Storage provider accepts a content. Requires signed storage provider account and its id.
  192. /// The LiaisonJudgement can be updated, but only by the liaison.
  193. pub(crate) fn accept_content(
  194. origin,
  195. storage_provider_id: StorageProviderId<T>,
  196. content_id: T::ContentId
  197. ) {
  198. <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
  199. // == MUTATION SAFE ==
  200. Self::update_content_judgement(&storage_provider_id, content_id, LiaisonJudgement::Accepted)?;
  201. <KnownContentIds<T>>::mutate(|ids| ids.push(content_id));
  202. Self::deposit_event(RawEvent::ContentAccepted(content_id, storage_provider_id));
  203. }
  204. /// Storage provider rejects a content. Requires signed storage provider account and its id.
  205. /// The LiaisonJudgement can be updated, but only by the liaison.
  206. pub(crate) fn reject_content(
  207. origin,
  208. storage_provider_id: StorageProviderId<T>,
  209. content_id: T::ContentId
  210. ) {
  211. <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
  212. // == MUTATION SAFE ==
  213. Self::update_content_judgement(&storage_provider_id, content_id, LiaisonJudgement::Rejected)?;
  214. Self::deposit_event(RawEvent::ContentRejected(content_id, storage_provider_id));
  215. }
  216. // Sudo methods
  217. /// Removes the content id from the list of known content ids. Requires root privileges.
  218. fn remove_known_content_id(origin, content_id: T::ContentId) {
  219. ensure_root(origin)?;
  220. // == MUTATION SAFE ==
  221. let upd_content_ids: Vec<T::ContentId> = Self::known_content_ids()
  222. .into_iter()
  223. .filter(|&id| id != content_id)
  224. .collect();
  225. <KnownContentIds<T>>::put(upd_content_ids);
  226. }
  227. /// Sets the content id from the list of known content ids. Requires root privileges.
  228. fn set_known_content_id(origin, content_ids: Vec<T::ContentId>) {
  229. ensure_root(origin)?;
  230. // == MUTATION SAFE ==
  231. <KnownContentIds<T>>::put(content_ids);
  232. }
  233. }
  234. }
  235. impl<T: Trait> Module<T> {
  236. fn update_content_judgement(
  237. storage_provider_id: &StorageProviderId<T>,
  238. content_id: T::ContentId,
  239. judgement: LiaisonJudgement,
  240. ) -> Result<(), Error> {
  241. let mut data = Self::data_object_by_content_id(&content_id).ok_or(Error::CidNotFound)?;
  242. // Make sure the liaison matches
  243. ensure!(data.liaison == *storage_provider_id, Error::LiaisonRequired);
  244. data.liaison_judgement = judgement;
  245. <DataObjectByContentId<T>>::insert(content_id, data);
  246. Ok(())
  247. }
  248. }
  249. /// Provides random storage provider id. We use it when assign the content to the storage provider.
  250. pub trait StorageProviderHelper<T: Trait> {
  251. /// Provides random storage provider id.
  252. fn get_random_storage_provider() -> Result<StorageProviderId<T>, &'static str>;
  253. }
  254. /// Content access helper.
  255. pub trait ContentIdExists<T: Trait> {
  256. /// Verifies the content existence.
  257. fn has_content(id: &T::ContentId) -> bool;
  258. /// Returns the data object for the provided content id.
  259. fn get_data_object(id: &T::ContentId) -> Result<DataObject<T>, &'static str>;
  260. }
  261. impl<T: Trait> ContentIdExists<T> for Module<T> {
  262. fn has_content(content_id: &T::ContentId) -> bool {
  263. Self::data_object_by_content_id(*content_id).is_some()
  264. }
  265. fn get_data_object(content_id: &T::ContentId) -> Result<DataObject<T>, &'static str> {
  266. match Self::data_object_by_content_id(*content_id) {
  267. Some(data) => Ok(data),
  268. None => Err(Error::LiaisonRequired.into()),
  269. }
  270. }
  271. }