downloads.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #![cfg_attr(not(feature = "std"), no_std)]
  2. /*
  3. * XXX This module is not really supposed to be used this way, and therefore also lacks tests.
  4. *
  5. * This is a straightforward implementation of the whitepaper's specs, and is intended to
  6. * be iterated over. Please don't use it as-is.
  7. */
  8. use crate::storage::data_directory::Trait as DDTrait;
  9. use crate::storage::data_object_storage_registry::Trait as DOSRTrait;
  10. use crate::traits::{ContentHasStorage, ContentIdExists};
  11. use parity_codec::Codec;
  12. use parity_codec_derive::{Decode, Encode};
  13. use rstd::prelude::*;
  14. use runtime_primitives::traits::{As, MaybeSerializeDebug, Member, SimpleArithmetic};
  15. use srml_support::{
  16. decl_event, decl_module, decl_storage, ensure, Parameter, StorageMap, StorageValue,
  17. };
  18. use system::{self, ensure_signed};
  19. pub trait Trait: timestamp::Trait + system::Trait + DOSRTrait + DDTrait {
  20. type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
  21. type DownloadSessionId: Parameter
  22. + Member
  23. + SimpleArithmetic
  24. + Codec
  25. + Default
  26. + Copy
  27. + As<usize>
  28. + As<u64>
  29. + MaybeSerializeDebug
  30. + PartialEq;
  31. type ContentHasStorage: ContentHasStorage<Self>;
  32. }
  33. static MSG_SESSION_NOT_FOUND: &str = "Download session with the given ID not found.";
  34. static MSG_SESSION_HAS_ENDED: &str = "Download session with the given ID has already ended.";
  35. static MSG_CONSUMER_REQUIRED: &str = "Download session can only be modified by the downloader.";
  36. static MSG_INVALID_TRANSMITTED_VALUE: &str = "Invalid update to transmitted bytes value.";
  37. static MSG_NEED_STORAGE_PROVIDER: &str =
  38. "Cannnot download without at least one active storage relationship.";
  39. const DEFAULT_FIRST_DOWNLOAD_SESSION_ID: u64 = 1;
  40. #[derive(Clone, Encode, Decode, PartialEq)]
  41. #[cfg_attr(feature = "std", derive(Debug))]
  42. pub enum DownloadState {
  43. Started,
  44. Ended,
  45. }
  46. impl Default for DownloadState {
  47. fn default() -> Self {
  48. DownloadState::Started
  49. }
  50. }
  51. #[derive(Clone, Encode, Decode, PartialEq)]
  52. #[cfg_attr(feature = "std", derive(Debug))]
  53. pub struct DownloadSession<T: Trait> {
  54. pub content_id: <T as DDTrait>::ContentId,
  55. pub consumer: T::AccountId,
  56. pub distributor: T::AccountId,
  57. // TODO use BlockAndTime instead
  58. pub initiated_at_block: T::BlockNumber,
  59. pub initiated_at_time: T::Moment,
  60. pub state: DownloadState,
  61. pub transmitted_bytes: u64,
  62. }
  63. decl_storage! {
  64. trait Store for Module<T: Trait> as DownloadSessions {
  65. // Start at this value
  66. pub FirstDownloadSessionId get(first_download_session_id) config(first_download_session_id): T::DownloadSessionId = T::DownloadSessionId::sa(DEFAULT_FIRST_DOWNLOAD_SESSION_ID);
  67. // Increment
  68. pub NextDownloadSessionId get(next_download_session_id) build(|config: &GenesisConfig<T>| config.first_download_session_id): T::DownloadSessionId = T::DownloadSessionId::sa(DEFAULT_FIRST_DOWNLOAD_SESSION_ID);
  69. // Mapping of Data object types
  70. pub DownloadSessions get(download_sessions): map T::DownloadSessionId => Option<DownloadSession<T>>;
  71. }
  72. }
  73. decl_event! {
  74. pub enum Event<T> where
  75. <T as DDTrait>::ContentId
  76. {
  77. // We send the content ID *only* because while we already leak download
  78. // session information by storing it on the public chain, there's no
  79. // need to advertise each download even more.
  80. DownloadStarted(ContentId),
  81. // Transmitted size is included in the ended event.
  82. DownloadEnded(ContentId, u64),
  83. }
  84. }
  85. decl_module! {
  86. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  87. fn deposit_event<T>() = default;
  88. // Origin starts a download from distributor. It's the origin's responsibility to
  89. // start this, and hand the session ID to the distributor as proof they did.
  90. pub fn start_download(origin, content_id: <T as DDTrait>::ContentId, from: T::AccountId) {
  91. // Origin can be anyone, it doesn't have to be a member.
  92. let who = ensure_signed(origin)?;
  93. // There has to be a storage relationship for the content ID and storage provider.
  94. ensure!(T::ContentHasStorage::is_ready_at_storage_provider(&content_id, &from), MSG_NEED_STORAGE_PROVIDER);
  95. // Let's create the entry then
  96. let new_id = Self::next_download_session_id();
  97. let session: DownloadSession<T> = DownloadSession {
  98. content_id: content_id.clone(),
  99. consumer: who,
  100. distributor: from.clone(),
  101. initiated_at_block: <system::Module<T>>::block_number(),
  102. initiated_at_time: <timestamp::Module<T>>::now(),
  103. state: DownloadState::Started,
  104. transmitted_bytes: 0u64,
  105. };
  106. <DownloadSessions<T>>::insert(new_id, session);
  107. <NextDownloadSessionId<T>>::mutate(|n| { *n += T::DownloadSessionId::sa(1); });
  108. // Fire off event
  109. Self::deposit_event(RawEvent::DownloadStarted(content_id));
  110. }
  111. // The downloader can also update the transmitted size, as long as it's
  112. // strictly larger.
  113. pub fn update_transmitted(origin, session_id: T::DownloadSessionId, transmitted_bytes: u64)
  114. {
  115. // Origin can be anyone, it doesn't have to be a member.
  116. let who = ensure_signed(origin)?;
  117. // Get session
  118. let mut session = Self::download_sessions(session_id).ok_or(MSG_SESSION_NOT_FOUND)?;
  119. // Ensure that the session hasn't ended yet.
  120. ensure!(session.state == DownloadState::Started, MSG_SESSION_HAS_ENDED);
  121. // Ensure that the origin is the consumer
  122. ensure!(session.consumer == who, MSG_CONSUMER_REQUIRED);
  123. // Ensure that the new transmitted size is larger than the old one
  124. ensure!(transmitted_bytes > session.transmitted_bytes, MSG_INVALID_TRANSMITTED_VALUE);
  125. // By fetching the content information, we can ensure that the transmitted
  126. // field also does not exceed the content size. Furthermore, we can
  127. // automatically detect when the download ended.
  128. let data_object = T::ContentIdExists::get_data_object(&session.content_id)?;
  129. ensure!(transmitted_bytes <= data_object.size, MSG_INVALID_TRANSMITTED_VALUE);
  130. let finished = transmitted_bytes == data_object.size;
  131. // Ok we can update the data.
  132. session.transmitted_bytes = transmitted_bytes;
  133. session.state = match finished {
  134. true => DownloadState::Ended,
  135. false => DownloadState::Started,
  136. };
  137. let content_id = session.content_id.clone();
  138. <DownloadSessions<T>>::insert(session_id, session);
  139. // Also announce if the download finished
  140. if finished {
  141. Self::deposit_event(RawEvent::DownloadEnded(content_id, transmitted_bytes));
  142. }
  143. }
  144. }
  145. }