downloads.rs 6.8 KB

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