lib.rs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. //! # Service discovery module
  2. //! Service discovery module for the Joystream platform supports the storage providers.
  3. //! It registers their 'pings' in the system with the expiration time, and stores the bootstrap
  4. //! nodes for the Colossus.
  5. //!
  6. //! ## Comments
  7. //!
  8. //! Service discovery module uses working group module to authorize actions. It is generally used by
  9. //! the Colossus service.
  10. //!
  11. //! ## Supported extrinsics
  12. //!
  13. //! - [set_ipns_id](./struct.Module.html#method.set_ipns_id) - Creates the ServiceProviderRecord to save an IPNS identity for the storage provider.
  14. //! - [unset_ipns_id](./struct.Module.html#method.unset_ipns_id) - Deletes the ServiceProviderRecord with the IPNS identity for the storage provider.
  15. //! - [set_default_lifetime](./struct.Module.html#method.set_default_lifetime) - Sets default lifetime for storage providers accounts info.
  16. //! - [set_bootstrap_endpoints](./struct.Module.html#method.set_bootstrap_endpoints) - Sets bootstrap endpoints for the Colossus.
  17. //!
  18. // Ensure we're `no_std` when compiling for Wasm.
  19. #![cfg_attr(not(feature = "std"), no_std)]
  20. mod mock;
  21. mod tests;
  22. use codec::{Decode, Encode};
  23. use common::Url;
  24. #[cfg(feature = "std")]
  25. use serde::{Deserialize, Serialize};
  26. use frame_support::{decl_event, decl_module, decl_storage, ensure};
  27. use sp_std::vec::Vec;
  28. use system::ensure_root;
  29. /*
  30. Although there is support for ed25519 keys as the IPNS identity key and we could potentially
  31. reuse the same key for the role account and ipns (and make this discovery module obselete)
  32. it is probably better to separate concerns.
  33. Why not to use a fixed size 32byte -> SHA256 hash of public key: because we would have to force
  34. specific key type on ipfs side.
  35. pub struct IPNSIdentity(pub [u8; 32]); // we loose the key type!
  36. pub type IPNSIdentity(pub u8, pub [u8; 32]); // we could add the keytype?
  37. can we use rust library in wasm runtime?
  38. https://github.com/multiformats/rust-multihash
  39. https://github.com/multiformats/multicodec/
  40. https://github.com/multiformats/multihash/
  41. */
  42. /// base58 encoded IPNS identity multihash codec
  43. pub type IPNSIdentity = Vec<u8>;
  44. // The storage working group instance alias.
  45. pub(crate) type StorageWorkingGroupInstance = working_group::Instance2;
  46. // Alias for storage working group.
  47. pub(crate) type StorageWorkingGroup<T> = working_group::Module<T, StorageWorkingGroupInstance>;
  48. /// Storage provider is a worker from the working_group module.
  49. pub type StorageProviderId<T> = working_group::WorkerId<T>;
  50. pub(crate) const MINIMUM_LIFETIME: u32 = 600; // 1hr assuming 6s block times
  51. pub(crate) const DEFAULT_LIFETIME: u32 = MINIMUM_LIFETIME * 24; // 24hr
  52. /// Defines the expiration date for the storage provider.
  53. #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
  54. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq)]
  55. pub struct ServiceProviderRecord<BlockNumber> {
  56. /// IPNS Identity.
  57. pub identity: IPNSIdentity,
  58. /// Block at which information expires.
  59. pub expires_at: BlockNumber,
  60. }
  61. /// The _Service discovery_ main _Trait_.
  62. pub trait Trait: system::Trait + working_group::Trait<StorageWorkingGroupInstance> {
  63. /// _Service discovery_ event type.
  64. type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
  65. }
  66. decl_storage! {
  67. trait Store for Module<T: Trait> as Discovery {
  68. /// Bootstrap endpoints maintained by root
  69. pub BootstrapEndpoints get(fn bootstrap_endpoints): Vec<Url>;
  70. /// Mapping of service providers' storage provider id to their ServiceProviderRecord
  71. pub AccountInfoByStorageProviderId get(fn account_info_by_storage_provider_id):
  72. map hasher(blake2_128_concat) StorageProviderId<T> => ServiceProviderRecord<T::BlockNumber>;
  73. /// Lifetime of an ServiceProviderRecord record in AccountInfoByAccountId map
  74. pub DefaultLifetime get(fn default_lifetime) config():
  75. T::BlockNumber = T::BlockNumber::from(DEFAULT_LIFETIME);
  76. }
  77. }
  78. decl_event! {
  79. /// _Service discovery_ events
  80. pub enum Event<T> where
  81. StorageProviderId = StorageProviderId<T>
  82. {
  83. /// Emits on updating of the account info.
  84. /// Params:
  85. /// - Id of the storage provider.
  86. /// - Id of the IPNS.
  87. AccountInfoUpdated(StorageProviderId, IPNSIdentity),
  88. /// Emits on removing of the account info.
  89. /// Params:
  90. /// - Id of the storage provider.
  91. AccountInfoRemoved(StorageProviderId),
  92. }
  93. }
  94. decl_module! {
  95. /// _Service discovery_ substrate module.
  96. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  97. /// Default deposit_event() handler
  98. fn deposit_event() = default;
  99. /// Creates the ServiceProviderRecord to save an IPNS identity for the storage provider.
  100. /// Requires signed storage provider credentials.
  101. #[weight = 10_000_000] // TODO: adjust weight
  102. pub fn set_ipns_id(
  103. origin,
  104. storage_provider_id: StorageProviderId<T>,
  105. id: Vec<u8>,
  106. ) {
  107. <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
  108. // TODO: ensure id is a valid base58 encoded IPNS identity
  109. //
  110. // == MUTATION SAFE ==
  111. //
  112. <AccountInfoByStorageProviderId<T>>::insert(storage_provider_id, ServiceProviderRecord {
  113. identity: id.clone(),
  114. expires_at: <system::Module<T>>::block_number() + Self::default_lifetime(),
  115. });
  116. Self::deposit_event(RawEvent::AccountInfoUpdated(storage_provider_id, id));
  117. }
  118. /// Deletes the ServiceProviderRecord with the IPNS identity for the storage provider.
  119. /// Requires signed storage provider credentials.
  120. #[weight = 10_000_000] // TODO: adjust weight
  121. pub fn unset_ipns_id(origin, storage_provider_id: StorageProviderId<T>) {
  122. <StorageWorkingGroup<T>>::ensure_worker_signed(origin, &storage_provider_id)?;
  123. // == MUTATION SAFE ==
  124. if <AccountInfoByStorageProviderId<T>>::contains_key(storage_provider_id) {
  125. <AccountInfoByStorageProviderId<T>>::remove(storage_provider_id);
  126. Self::deposit_event(RawEvent::AccountInfoRemoved(storage_provider_id));
  127. }
  128. }
  129. // Privileged methods
  130. /// Sets default lifetime for storage providers accounts info. Requires root privileges.
  131. #[weight = 10_000_000] // TODO: adjust weight
  132. pub fn set_default_lifetime(origin, lifetime: T::BlockNumber) {
  133. ensure_root(origin)?;
  134. ensure!(lifetime >= T::BlockNumber::from(MINIMUM_LIFETIME),
  135. "discovery: default lifetime must be gte minimum lifetime");
  136. // == MUTATION SAFE ==
  137. <DefaultLifetime<T>>::put(lifetime);
  138. }
  139. /// Sets bootstrap endpoints for the Colossus. Requires root privileges.
  140. #[weight = 10_000_000] // TODO: adjust weight
  141. pub fn set_bootstrap_endpoints(origin, endpoints: Vec<Url>) {
  142. ensure_root(origin)?;
  143. // == MUTATION SAFE ==
  144. BootstrapEndpoints::put(endpoints);
  145. }
  146. }
  147. }
  148. impl<T: Trait> Module<T> {
  149. /// Verifies that account info for the storage provider is still valid.
  150. pub fn is_account_info_expired(storage_provider_id: &StorageProviderId<T>) -> bool {
  151. !<AccountInfoByStorageProviderId<T>>::contains_key(storage_provider_id)
  152. || <system::Module<T>>::block_number()
  153. > <AccountInfoByStorageProviderId<T>>::get(storage_provider_id).expires_at
  154. }
  155. }