data_directory.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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 frame_support::dispatch::DispatchResult;
  24. use frame_support::traits::Get;
  25. use frame_support::{decl_error, decl_event, decl_module, decl_storage, ensure};
  26. use sp_std::collections::btree_map::BTreeMap;
  27. use sp_std::vec::Vec;
  28. use system::ensure_root;
  29. #[cfg(feature = "std")]
  30. use serde::{Deserialize, Serialize};
  31. use common::origin::ActorOriginValidator;
  32. pub use common::storage::{ContentParameters, StorageObjectOwner};
  33. pub(crate) use common::BlockAndTime;
  34. use crate::data_object_type_registry;
  35. use crate::data_object_type_registry::IsActiveDataObjectType;
  36. use crate::*;
  37. /// The _Data directory_ main _Trait_.
  38. pub trait Trait:
  39. pallet_timestamp::Trait
  40. + system::Trait
  41. + data_object_type_registry::Trait
  42. + membership::Trait
  43. + working_group::Trait<StorageWorkingGroupInstance>
  44. + common::MembershipTypes
  45. + common::StorageOwnership
  46. {
  47. /// _Data directory_ event type.
  48. type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
  49. /// Provides random storage provider id.
  50. type StorageProviderHelper: StorageProviderHelper<Self>;
  51. /// Active data object type validator.
  52. type IsActiveDataObjectType: data_object_type_registry::IsActiveDataObjectType<Self>;
  53. /// Validates member id and origin combination.
  54. type MemberOriginValidator: ActorOriginValidator<Self::Origin, MemberId<Self>, Self::AccountId>;
  55. type MaxObjectsPerInjection: Get<u32>;
  56. /// Default content quota for all actors.
  57. type DefaultQuota: Get<Quota>;
  58. }
  59. decl_error! {
  60. /// _Data object storage registry_ module predefined errors.
  61. pub enum Error for Module<T: Trait>{
  62. /// Content with this ID not found.
  63. CidNotFound,
  64. /// Only the liaison for the content may modify its status.
  65. LiaisonRequired,
  66. /// Cannot create content for inactive or missing data object type.
  67. DataObjectTypeMustBeActive,
  68. /// "Data object already added under this content id".
  69. DataObjectAlreadyAdded,
  70. /// Require root origin in extrinsics.
  71. RequireRootOrigin,
  72. /// DataObject Injection Failed. Too Many DataObjects.
  73. DataObjectsInjectionExceededLimit,
  74. /// Contant uploading failed. Actor quota objects limit exceeded.
  75. QuotaObjectsLimitExceeded,
  76. /// Contant uploading failed. Actor quota size limit exceeded.
  77. QuotaSizeLimitExceeded,
  78. /// Quota size limit upper bound exceeded
  79. QuotaSizeLimitUpperBoundExceeded,
  80. /// Quota objects limit upper bound exceeded
  81. QuotaObjectsLimitUpperBoundExceeded,
  82. /// Contant uploading failed. Actor quota size limit exceeded.
  83. GlobalQuotaSizeLimitExceeded,
  84. /// Contant uploading failed. Actor quota objects limit exceeded.
  85. GlobalQuotaObjectsLimitExceeded,
  86. }
  87. }
  88. /// The decision of the storage provider when it acts as liaison.
  89. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  90. #[derive(Clone, Encode, Decode, PartialEq, Debug)]
  91. pub enum LiaisonJudgement {
  92. /// Content awaits for a judgment.
  93. Pending,
  94. /// Content accepted.
  95. Accepted,
  96. /// Content rejected.
  97. Rejected,
  98. }
  99. impl Default for LiaisonJudgement {
  100. fn default() -> Self {
  101. LiaisonJudgement::Pending
  102. }
  103. }
  104. /// Alias for DataObjectInternal
  105. pub type DataObject<T> = DataObjectInternal<
  106. MemberId<T>,
  107. ChannelId<T>,
  108. DAOId<T>,
  109. <T as system::Trait>::BlockNumber,
  110. <T as pallet_timestamp::Trait>::Moment,
  111. DataObjectTypeId<T>,
  112. StorageProviderId<T>,
  113. >;
  114. /// Manages content ids, type and storage provider decision about it.
  115. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  116. #[derive(Clone, Encode, Decode, PartialEq, Debug)]
  117. pub struct DataObjectInternal<
  118. MemberId,
  119. ChannelId,
  120. DAOId,
  121. BlockNumber,
  122. Moment,
  123. DataObjectTypeId,
  124. StorageProviderId,
  125. > {
  126. /// Content owner.
  127. pub owner: StorageObjectOwner<MemberId, ChannelId, DAOId>,
  128. /// Content added at.
  129. pub added_at: BlockAndTime<BlockNumber, Moment>,
  130. /// Content type id.
  131. pub type_id: DataObjectTypeId,
  132. /// Content size in bytes.
  133. pub size: u64,
  134. /// Storage provider id of the liaison.
  135. pub liaison: StorageProviderId,
  136. /// Storage provider as liaison judgment.
  137. pub liaison_judgement: LiaisonJudgement,
  138. /// IPFS content id.
  139. pub ipfs_content_id: Vec<u8>,
  140. }
  141. #[derive(Clone, Copy)]
  142. pub struct Voucher {
  143. pub size: u64,
  144. pub objects: u64,
  145. }
  146. /// Uploading quota for StorageObjectOwner
  147. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  148. #[derive(Clone, Copy, Encode, Decode, PartialEq, Eq, Debug, Default)]
  149. pub struct Quota {
  150. // Total objects size limit per StorageObjectOwner
  151. pub size_limit: u64,
  152. // Total objects number limit per StorageObjectOwner
  153. pub objects_limit: u64,
  154. pub size_used: u64,
  155. pub objects_used: u64,
  156. }
  157. impl Quota {
  158. /// Create new quota with provided size & objects limits
  159. pub const fn new(size_limit: u64, objects_limit: u64) -> Self {
  160. Self {
  161. size_limit,
  162. objects_limit,
  163. size_used: 0,
  164. objects_used: 0,
  165. }
  166. }
  167. /// Calculate free quota
  168. pub fn calculate_voucher(&self) -> Voucher {
  169. Voucher {
  170. size: self.size_limit - self.size_used,
  171. objects: self.objects_limit - self.objects_used,
  172. }
  173. }
  174. pub fn fill_quota(self, voucher: Voucher) -> Self {
  175. Self {
  176. size_used: self.size_used + voucher.size,
  177. objects_used: self.objects_used + voucher.objects,
  178. ..self
  179. }
  180. }
  181. pub fn release_quota(self, voucher: Voucher) -> Self {
  182. Self {
  183. size_used: self.size_used - voucher.size,
  184. objects_used: self.objects_used - voucher.objects,
  185. ..self
  186. }
  187. }
  188. pub fn set_new_size_limit(&mut self, new_size_limit: u64) {
  189. self.size_limit = new_size_limit;
  190. }
  191. pub fn set_new_objects_limit(&mut self, new_objects_limit: u64) {
  192. self.objects_limit = new_objects_limit;
  193. }
  194. }
  195. /// A map collection of unique DataObjects keyed by the ContentId
  196. pub type DataObjectsMap<T> = BTreeMap<ContentId<T>, DataObject<T>>;
  197. decl_storage! {
  198. trait Store for Module<T: Trait> as DataDirectory {
  199. /// List of ids known to the system.
  200. pub KnownContentIds get(fn known_content_ids) config(): Vec<ContentId<T>> = Vec::new();
  201. /// Maps data objects by their content id.
  202. pub DataObjectByContentId get(fn data_object_by_content_id) config():
  203. map hasher(blake2_128_concat) T::ContentId => Option<DataObject<T>>;
  204. /// Maps storage owner to it`s quota. Created when the first upload by the new actor occured.
  205. pub Quotas get(fn quotas) config():
  206. map hasher(blake2_128_concat) StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>> => Quota;
  207. /// Upper bound for the Quota size limit.
  208. pub QuotaSizeLimitUpperBound get(fn quota_size_limit_upper_bound) config(): u64;
  209. /// Upper bound for the Quota objects number limit.
  210. pub QuotaObjectsLimitUpperBound get(fn quota_objects_limit_upper_bound) config(): u64;
  211. /// Global quota.
  212. pub GlobalQuota get(fn global_quota) config(): Quota;
  213. }
  214. }
  215. decl_event! {
  216. /// _Data directory_ events
  217. pub enum Event<T> where
  218. StorageObjectOwner = StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
  219. StorageProviderId = StorageProviderId<T>,
  220. Content = Vec<ContentParameters<ContentId<T>, DataObjectTypeId<T>>>,
  221. ContentId = ContentId<T>,
  222. QuotaLimit = u64
  223. {
  224. /// Emits on adding of the content.
  225. /// Params:
  226. /// - Content parameters representation.
  227. /// - StorageObjectOwner enum.
  228. ContentAdded(Content, StorageObjectOwner),
  229. /// Emits when the storage provider accepts a content.
  230. /// Params:
  231. /// - Id of the relationship.
  232. /// - Id of the storage provider.
  233. ContentAccepted(ContentId, StorageProviderId),
  234. /// Emits when the storage provider rejects a content.
  235. /// Params:
  236. /// - Id of the relationship.
  237. /// - Id of the storage provider.
  238. ContentRejected(ContentId, StorageProviderId),
  239. /// Emits when the storage object owner quota size limit update performed.
  240. /// Params:
  241. /// - StorageObjectOwner enum.
  242. /// - quota size limit.
  243. StorageObjectOwnerQuotaSizeLimitUpdated(StorageObjectOwner, QuotaLimit),
  244. /// Emits when the storage object owner quota objects limit update performed.
  245. /// Params:
  246. /// - StorageObjectOwner enum.
  247. /// - quota objects limit.
  248. StorageObjectOwnerQuotaObjectsLimitUpdated(StorageObjectOwner, QuotaLimit),
  249. }
  250. }
  251. decl_module! {
  252. /// _Data directory_ substrate module.
  253. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  254. /// Default deposit_event() handler
  255. fn deposit_event() = default;
  256. /// Predefined errors.
  257. type Error = Error<T>;
  258. /// Maximum objects allowed per inject_data_objects() transaction
  259. const MaxObjectsPerInjection: u32 = T::MaxObjectsPerInjection::get();
  260. /// Adds the content to the system. The created DataObject
  261. /// awaits liaison to accept or reject it.
  262. #[weight = 10_000_000] // TODO: adjust weight
  263. pub fn add_content(
  264. origin,
  265. owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
  266. content: Vec<ContentParameters<ContentId<T>, DataObjectTypeId<T>>>
  267. ) {
  268. // Ensure given origin can perform operation under specific storage object owner
  269. Self::ensure_storage_object_owner_origin(origin, &owner)?;
  270. Self::ensure_content_is_valid(&content)?;
  271. let owner_quota = Self::get_quota(&owner);
  272. // Ensure owner quota constraints satisfied.
  273. // Calculate upload voucher
  274. let upload_voucher = Self::ensure_owner_quota_constraints_satisfied(owner_quota, &content)?;
  275. // Ensure global quota constraints satisfied.
  276. Self::ensure_global_quota_constraints_satisfied(upload_voucher)?;
  277. let liaison = T::StorageProviderHelper::get_random_storage_provider()?;
  278. //
  279. // == MUTATION SAFE ==
  280. //
  281. // Let's create the entry then
  282. Self::upload_content(owner_quota, upload_voucher, liaison, content.clone(), owner.clone());
  283. Self::deposit_event(RawEvent::ContentAdded(content, owner));
  284. }
  285. /// Updates storage object owner quota objects limit. Requires leader privileges.
  286. #[weight = 10_000_000] // TODO: adjust weight
  287. pub fn update_storage_object_owner_quota_objects_limit(
  288. origin,
  289. abstract_owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
  290. new_quota_objects_limit: u64
  291. ) {
  292. <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
  293. ensure!(new_quota_objects_limit <= Self::quota_objects_limit_upper_bound(), Error::<T>::QuotaSizeLimitUpperBoundExceeded);
  294. //
  295. // == MUTATION SAFE ==
  296. //
  297. if <Quotas<T>>::contains_key(&abstract_owner) {
  298. <Quotas<T>>::mutate(&abstract_owner, |quota| {
  299. quota.set_new_objects_limit(new_quota_objects_limit);
  300. });
  301. } else {
  302. let mut quota = T::DefaultQuota::get();
  303. quota.set_new_objects_limit(new_quota_objects_limit);
  304. <Quotas<T>>::insert(&abstract_owner, quota);
  305. };
  306. Self::deposit_event(RawEvent::StorageObjectOwnerQuotaObjectsLimitUpdated(abstract_owner, new_quota_objects_limit));
  307. }
  308. /// Updates storage object owner quota size limit. Requires leader privileges.
  309. #[weight = 10_000_000] // TODO: adjust weight
  310. pub fn update_storage_object_owner_quota_size_limit(
  311. origin,
  312. abstract_owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
  313. new_quota_size_limit: u64
  314. ) {
  315. <StorageWorkingGroup<T>>::ensure_origin_is_active_leader(origin)?;
  316. ensure!(new_quota_size_limit <= Self::quota_size_limit_upper_bound(), Error::<T>::QuotaObjectsLimitUpperBoundExceeded);
  317. //
  318. // == MUTATION SAFE ==
  319. //
  320. if <Quotas<T>>::contains_key(&abstract_owner) {
  321. <Quotas<T>>::mutate(&abstract_owner, |quota| {
  322. quota.set_new_size_limit(new_quota_size_limit);
  323. });
  324. } else {
  325. let mut quota = T::DefaultQuota::get();
  326. quota.set_new_size_limit(new_quota_size_limit);
  327. <Quotas<T>>::insert(&abstract_owner, quota);
  328. };
  329. Self::deposit_event(RawEvent::StorageObjectOwnerQuotaSizeLimitUpdated(abstract_owner, new_quota_size_limit));
  330. }
  331. /// Storage provider accepts a content. Requires signed storage provider account and its id.
  332. /// The LiaisonJudgement can be updated, but only by the liaison.
  333. #[weight = 10_000_000] // TODO: adjust weight
  334. pub(crate) fn accept_content(
  335. origin,
  336. storage_provider_id: StorageProviderId<T>,
  337. content_id: T::ContentId
  338. ) {
  339. <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
  340. // == MUTATION SAFE ==
  341. Self::update_content_judgement(&storage_provider_id, content_id, LiaisonJudgement::Accepted)?;
  342. <KnownContentIds<T>>::mutate(|ids| ids.push(content_id));
  343. Self::deposit_event(RawEvent::ContentAccepted(content_id, storage_provider_id));
  344. }
  345. /// Storage provider rejects a content. Requires signed storage provider account and its id.
  346. /// The LiaisonJudgement can be updated, but only by the liaison.
  347. #[weight = 10_000_000] // TODO: adjust weight
  348. pub(crate) fn reject_content(
  349. origin,
  350. storage_provider_id: StorageProviderId<T>,
  351. content_id: T::ContentId
  352. ) {
  353. <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
  354. // == MUTATION SAFE ==
  355. Self::update_content_judgement(&storage_provider_id, content_id, LiaisonJudgement::Rejected)?;
  356. Self::deposit_event(RawEvent::ContentRejected(content_id, storage_provider_id));
  357. }
  358. // Sudo methods
  359. /// Removes the content id from the list of known content ids. Requires root privileges.
  360. #[weight = 10_000_000] // TODO: adjust weight
  361. fn remove_known_content_id(origin, content_id: T::ContentId) {
  362. ensure_root(origin)?;
  363. // == MUTATION SAFE ==
  364. let upd_content_ids: Vec<T::ContentId> = Self::known_content_ids()
  365. .into_iter()
  366. .filter(|&id| id != content_id)
  367. .collect();
  368. <KnownContentIds<T>>::put(upd_content_ids);
  369. }
  370. /// Injects a set of data objects and their corresponding content id into the directory.
  371. /// The operation is "silent" - no events will be emitted as objects are added.
  372. /// The number of objects that can be added per call is limited to prevent the dispatch
  373. /// from causing the block production to fail if it takes too much time to process.
  374. /// Existing data objects will be overwritten.
  375. #[weight = 10_000_000] // TODO: adjust weight
  376. pub(crate) fn inject_data_objects(origin, objects: DataObjectsMap<T>) {
  377. ensure_root(origin)?;
  378. // Must provide something to inject
  379. ensure!(objects.len() <= T::MaxObjectsPerInjection::get() as usize, Error::<T>::DataObjectsInjectionExceededLimit);
  380. for (id, object) in objects.into_iter() {
  381. // append to known content ids
  382. // duplicates will be removed at the end
  383. <KnownContentIds<T>>::mutate(|ids| ids.push(id));
  384. <DataObjectByContentId<T>>::insert(id, object);
  385. }
  386. // remove duplicate ids
  387. <KnownContentIds<T>>::mutate(|ids| {
  388. ids.sort();
  389. ids.dedup();
  390. });
  391. }
  392. }
  393. }
  394. impl<T: Trait> Module<T> {
  395. // Ensure given origin can perform operation under specific storage object owner
  396. fn ensure_storage_object_owner_origin(
  397. origin: T::Origin,
  398. owner: &StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
  399. ) -> DispatchResult {
  400. if let StorageObjectOwner::Member(member_id) = owner {
  401. T::MemberOriginValidator::ensure_actor_origin(origin, *member_id)?;
  402. } else {
  403. ensure_root(origin)?;
  404. };
  405. Ok(())
  406. }
  407. // Get owner quota if exists, otherwise return default one.
  408. fn get_quota(owner: &StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>) -> Quota {
  409. if <Quotas<T>>::contains_key(owner) {
  410. Self::quotas(owner)
  411. } else {
  412. T::DefaultQuota::get()
  413. }
  414. }
  415. // Ensure owner quota constraints satisfied, returns total object length and total size voucher for this upload.
  416. fn ensure_owner_quota_constraints_satisfied(
  417. owner_quota: Quota,
  418. content: &[ContentParameters<T::ContentId, DataObjectTypeId<T>>],
  419. ) -> Result<Voucher, Error<T>> {
  420. let owner_quota_voucher = owner_quota.calculate_voucher();
  421. // Ensure total content length is less or equal then available per given owner quota
  422. let content_length = content.len() as u64;
  423. ensure!(
  424. owner_quota_voucher.objects >= content_length,
  425. Error::<T>::QuotaObjectsLimitExceeded
  426. );
  427. // Ensure total content size is less or equal then available per given owner quota
  428. let content_size = content
  429. .iter()
  430. .fold(0, |total_size, content| total_size + content.size);
  431. ensure!(
  432. owner_quota_voucher.size >= content_size,
  433. Error::<T>::QuotaSizeLimitExceeded
  434. );
  435. Ok(Voucher {
  436. size: content_size,
  437. objects: content_length,
  438. })
  439. }
  440. // Ensures global quota constraints satisfied.
  441. fn ensure_global_quota_constraints_satisfied(upload_voucher: Voucher) -> DispatchResult {
  442. let global_quota_voucher = Self::global_quota().calculate_voucher();
  443. ensure!(
  444. global_quota_voucher.objects >= upload_voucher.objects,
  445. Error::<T>::GlobalQuotaObjectsLimitExceeded
  446. );
  447. ensure!(
  448. global_quota_voucher.size >= upload_voucher.size,
  449. Error::<T>::GlobalQuotaSizeLimitExceeded
  450. );
  451. Ok(())
  452. }
  453. // Complete content upload, update quotas
  454. fn upload_content(
  455. owner_quota: Quota,
  456. upload_voucher: Voucher,
  457. liaison: StorageProviderId<T>,
  458. multi_content: Vec<ContentParameters<T::ContentId, DataObjectTypeId<T>>>,
  459. owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
  460. ) {
  461. for content in multi_content {
  462. let data: DataObject<T> = DataObjectInternal {
  463. type_id: content.type_id,
  464. size: content.size,
  465. added_at: common::current_block_time::<T>(),
  466. owner: owner.clone(),
  467. liaison,
  468. liaison_judgement: LiaisonJudgement::Pending,
  469. ipfs_content_id: content.ipfs_content_id,
  470. };
  471. <DataObjectByContentId<T>>::insert(content.content_id, data);
  472. }
  473. // Updade or create owner quota.
  474. <Quotas<T>>::insert(owner, owner_quota.fill_quota(upload_voucher));
  475. // Update global quota
  476. <GlobalQuota>::mutate(|global_quota| global_quota.fill_quota(upload_voucher));
  477. }
  478. fn ensure_content_is_valid(
  479. multi_content: &[ContentParameters<T::ContentId, DataObjectTypeId<T>>],
  480. ) -> DispatchResult {
  481. for content in multi_content {
  482. ensure!(
  483. T::IsActiveDataObjectType::is_active_data_object_type(&content.type_id),
  484. Error::<T>::DataObjectTypeMustBeActive
  485. );
  486. ensure!(
  487. !<DataObjectByContentId<T>>::contains_key(&content.content_id),
  488. Error::<T>::DataObjectAlreadyAdded
  489. );
  490. }
  491. Ok(())
  492. }
  493. fn update_content_judgement(
  494. storage_provider_id: &StorageProviderId<T>,
  495. content_id: T::ContentId,
  496. judgement: LiaisonJudgement,
  497. ) -> DispatchResult {
  498. let mut data =
  499. Self::data_object_by_content_id(&content_id).ok_or(Error::<T>::CidNotFound)?;
  500. // Make sure the liaison matches
  501. ensure!(
  502. data.liaison == *storage_provider_id,
  503. Error::<T>::LiaisonRequired
  504. );
  505. data.liaison_judgement = judgement;
  506. <DataObjectByContentId<T>>::insert(content_id, data);
  507. Ok(())
  508. }
  509. }
  510. /// Provides random storage provider id. We use it when assign the content to the storage provider.
  511. pub trait StorageProviderHelper<T: Trait> {
  512. /// Provides random storage provider id.
  513. fn get_random_storage_provider() -> Result<StorageProviderId<T>, &'static str>;
  514. }
  515. /// Content access helper.
  516. pub trait ContentIdExists<T: Trait> {
  517. /// Verifies the content existence.
  518. fn has_content(id: &T::ContentId) -> bool;
  519. /// Returns the data object for the provided content id.
  520. fn get_data_object(id: &T::ContentId) -> Result<DataObject<T>, &'static str>;
  521. }
  522. impl<T: Trait> ContentIdExists<T> for Module<T> {
  523. fn has_content(content_id: &T::ContentId) -> bool {
  524. Self::data_object_by_content_id(*content_id).is_some()
  525. }
  526. fn get_data_object(content_id: &T::ContentId) -> Result<DataObject<T>, &'static str> {
  527. match Self::data_object_by_content_id(*content_id) {
  528. Some(data) => Ok(data),
  529. None => Err(Error::<T>::LiaisonRequired.into()),
  530. }
  531. }
  532. }
  533. impl<T: Trait> common::storage::StorageSystem<T> for Module<T> {
  534. fn atomically_add_content(
  535. owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
  536. content: Vec<ContentParameters<T::ContentId, DataObjectTypeId<T>>>,
  537. ) -> DispatchResult {
  538. Self::ensure_content_is_valid(&content)?;
  539. let liaison = T::StorageProviderHelper::get_random_storage_provider()?;
  540. let owner_quota = Self::get_quota(&owner);
  541. let upload_voucher = Self::ensure_owner_quota_constraints_satisfied(owner_quota, &content)?;
  542. Self::upload_content(owner_quota, upload_voucher, liaison, content, owner);
  543. Ok(())
  544. }
  545. fn can_add_content(
  546. owner: StorageObjectOwner<MemberId<T>, ChannelId<T>, DAOId<T>>,
  547. content: Vec<ContentParameters<T::ContentId, DataObjectTypeId<T>>>,
  548. ) -> DispatchResult {
  549. T::StorageProviderHelper::get_random_storage_provider()?;
  550. let owner_quota = Self::get_quota(&owner);
  551. Self::ensure_owner_quota_constraints_satisfied(owner_quota, &content)?;
  552. Self::ensure_content_is_valid(&content)
  553. }
  554. }