utils.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import { DatabaseManager } from '@joystream/hydra-common'
  2. import { UploadParameters } from '@joystream/types/augment'
  3. import { registry } from '@joystream/types'
  4. import { DataObjectCreationParameters as ObjectCreationParams } from '@joystream/types/storage'
  5. import {
  6. DataObjectTypeUnknown,
  7. StorageBag,
  8. StorageDataObject,
  9. StorageSystemParameters,
  10. StorageBagOwner,
  11. StorageBagOwnerChannel,
  12. StorageBagOwnerCouncil,
  13. StorageBagOwnerMember,
  14. StorageBagOwnerWorkingGroup,
  15. StorageBucket,
  16. DistributionBucketOperator,
  17. DistributionBucketFamily,
  18. } from 'query-node/dist/model'
  19. import BN from 'bn.js'
  20. import { bytesToString, inconsistentState, getById, RelationsArr } from '../common'
  21. import { In } from 'typeorm'
  22. import { unsetAssetRelations } from '../content/utils'
  23. import { BTreeSet } from '@polkadot/types'
  24. import _ from 'lodash'
  25. import {
  26. DataObjectId,
  27. BagId,
  28. DynamicBagId,
  29. StaticBagId,
  30. DistributionBucketId,
  31. DistributionBucketFamilyId,
  32. DistributionBucketIndex,
  33. WorkerId,
  34. } from '@joystream/types/augment/all'
  35. import { Balance } from '@polkadot/types/interfaces'
  36. export async function getDataObjectsInBag(
  37. store: DatabaseManager,
  38. bagId: BagId,
  39. dataObjectIds: BTreeSet<DataObjectId>
  40. ): Promise<StorageDataObject[]> {
  41. const dataObjects = await store.getMany(StorageDataObject, {
  42. where: {
  43. id: In(Array.from(dataObjectIds).map((id) => id.toString())),
  44. storageBag: { id: getBagId(bagId) },
  45. },
  46. })
  47. if (dataObjects.length !== Array.from(dataObjectIds).length) {
  48. throw new Error(
  49. `Missing data objects: ${_.difference(
  50. Array.from(dataObjectIds).map((id) => id.toString()),
  51. dataObjects.map((o) => o.id)
  52. )} in bag ${getBagId(bagId)}`
  53. )
  54. }
  55. return dataObjects
  56. }
  57. export function getStaticBagOwner(bagId: StaticBagId): typeof StorageBagOwner {
  58. if (bagId.isCouncil) {
  59. return new StorageBagOwnerCouncil()
  60. } else if (bagId.isWorkingGroup) {
  61. const owner = new StorageBagOwnerWorkingGroup()
  62. owner.workingGroupId = bagId.asWorkingGroup.toString().toLowerCase()
  63. return owner
  64. } else {
  65. throw new Error(`Unexpected static bag type: ${bagId.type}`)
  66. }
  67. }
  68. export function getDynamicBagOwner(bagId: DynamicBagId): typeof StorageBagOwner {
  69. if (bagId.isChannel) {
  70. const owner = new StorageBagOwnerChannel()
  71. owner.channelId = bagId.asChannel.toNumber()
  72. return owner
  73. } else if (bagId.isMember) {
  74. const owner = new StorageBagOwnerMember()
  75. owner.memberId = bagId.asMember.toNumber()
  76. return owner
  77. } else {
  78. throw new Error(`Unexpected dynamic bag type: ${bagId.type}`)
  79. }
  80. }
  81. export function getStaticBagId(bagId: StaticBagId): string {
  82. if (bagId.isCouncil) {
  83. return `static:council`
  84. } else if (bagId.isWorkingGroup) {
  85. return `static:wg:${bagId.asWorkingGroup.type.toLowerCase()}`
  86. } else {
  87. throw new Error(`Unexpected static bag type: ${bagId.type}`)
  88. }
  89. }
  90. export function getDynamicBagId(bagId: DynamicBagId): string {
  91. if (bagId.isChannel) {
  92. return `dynamic:channel:${bagId.asChannel.toString()}`
  93. } else if (bagId.isMember) {
  94. return `dynamic:member:${bagId.asMember.toString()}`
  95. } else {
  96. throw new Error(`Unexpected dynamic bag type: ${bagId.type}`)
  97. }
  98. }
  99. export function getBagId(bagId: BagId): string {
  100. return bagId.isStatic ? getStaticBagId(bagId.asStatic) : getDynamicBagId(bagId.asDynamic)
  101. }
  102. export async function getDynamicBag(
  103. store: DatabaseManager,
  104. bagId: DynamicBagId,
  105. relations?: RelationsArr<StorageBag>
  106. ): Promise<StorageBag> {
  107. return getById(store, StorageBag, getDynamicBagId(bagId), relations)
  108. }
  109. export async function getStaticBag(
  110. store: DatabaseManager,
  111. bagId: StaticBagId,
  112. relations?: RelationsArr<StorageBag>
  113. ): Promise<StorageBag> {
  114. const id = getStaticBagId(bagId)
  115. const bag = await store.get(StorageBag, { where: { id }, relations })
  116. if (!bag) {
  117. console.log(`Creating new static bag: ${id}`)
  118. const newBag = new StorageBag({
  119. id,
  120. owner: getStaticBagOwner(bagId),
  121. })
  122. await store.save<StorageBag>(newBag)
  123. return newBag
  124. }
  125. return bag
  126. }
  127. export async function getBag(
  128. store: DatabaseManager,
  129. bagId: BagId,
  130. relations?: RelationsArr<StorageBag>
  131. ): Promise<StorageBag> {
  132. return bagId.isStatic
  133. ? getStaticBag(store, bagId.asStatic, relations)
  134. : getDynamicBag(store, bagId.asDynamic, relations)
  135. }
  136. export async function getDistributionBucketOperatorWithMetadata(
  137. store: DatabaseManager,
  138. id: string
  139. ): Promise<DistributionBucketOperator> {
  140. const operator = await store.get(DistributionBucketOperator, {
  141. where: { id },
  142. relations: ['metadata', 'metadata.nodeLocation', 'metadata.nodeLocation.coordinates'],
  143. })
  144. if (!operator) {
  145. throw new Error(`DistributionBucketOperator not found by id: ${id}`)
  146. }
  147. return operator
  148. }
  149. export async function getStorageBucketWithOperatorMetadata(store: DatabaseManager, id: string): Promise<StorageBucket> {
  150. const bucket = await store.get(StorageBucket, {
  151. where: { id },
  152. relations: ['operatorMetadata', 'operatorMetadata.nodeLocation', 'operatorMetadata.nodeLocation.coordinates'],
  153. })
  154. if (!bucket) {
  155. throw new Error(`StorageBucket not found by id: ${id}`)
  156. }
  157. return bucket
  158. }
  159. export async function getDistributionBucketFamilyWithMetadata(
  160. store: DatabaseManager,
  161. id: string
  162. ): Promise<DistributionBucketFamily> {
  163. const family = await store.get(DistributionBucketFamily, {
  164. where: { id },
  165. relations: ['metadata', 'metadata.areas'],
  166. })
  167. if (!family) {
  168. throw new Error(`DistributionBucketFamily not found by id: ${id}`)
  169. }
  170. return family
  171. }
  172. export async function getStorageSystem(store: DatabaseManager): Promise<StorageSystemParameters> {
  173. const storageSystem = await store.get(StorageSystemParameters, {})
  174. if (!storageSystem) {
  175. throw new Error('Storage system entity is missing!')
  176. }
  177. return storageSystem
  178. }
  179. export async function createDataObjects(
  180. store: DatabaseManager,
  181. uploadParams: UploadParameters,
  182. deletionPrize: Balance,
  183. objectIds?: BN[]
  184. ): Promise<StorageDataObject[]> {
  185. const storageSystem = await getStorageSystem(store)
  186. const { objectCreationList, bagId } = uploadParams
  187. const storageBag = await getBag(store, bagId)
  188. const dataObjects = objectCreationList.map((objectParams, i) => {
  189. const params = new ObjectCreationParams(registry, objectParams.toJSON() as any)
  190. const objectId = objectIds ? objectIds[i] : storageSystem.nextDataObjectId
  191. const object = new StorageDataObject({
  192. id: objectId.toString(),
  193. isAccepted: false,
  194. ipfsHash: bytesToString(objectParams.ipfsContentId),
  195. size: new BN(params.getField('size').toString()),
  196. type: new DataObjectTypeUnknown(),
  197. deletionPrize,
  198. storageBag,
  199. })
  200. if (objectId.gte(storageSystem.nextDataObjectId)) {
  201. storageSystem.nextDataObjectId = objectId.addn(1)
  202. }
  203. return object
  204. })
  205. await Promise.all(dataObjects.map((o) => store.save<StorageDataObject>(o)))
  206. await store.save<StorageSystemParameters>(storageSystem)
  207. return dataObjects
  208. }
  209. export async function getMostRecentlyCreatedDataObjects(
  210. store: DatabaseManager,
  211. numberOfObjects: number
  212. ): Promise<StorageDataObject[]> {
  213. const storageSystem = await getStorageSystem(store)
  214. const objectIds = Array.from({ length: numberOfObjects }, (v, k) =>
  215. storageSystem.nextDataObjectId.subn(k + 1).toString()
  216. )
  217. const objects = await store.getMany(StorageDataObject, { where: { id: In(objectIds) } })
  218. if (objects.length < numberOfObjects) {
  219. inconsistentState(`Could not get ${numberOfObjects} most recently created data objects`, {
  220. expected: numberOfObjects,
  221. got: objects.length,
  222. })
  223. }
  224. return objects.sort((a, b) => new BN(a.id).cmp(new BN(b.id)))
  225. }
  226. export async function removeDataObject(store: DatabaseManager, object: StorageDataObject): Promise<void> {
  227. await unsetAssetRelations(store, object)
  228. await store.remove<StorageDataObject>(object)
  229. }
  230. export function distributionBucketId(runtimeBucketId: DistributionBucketId): string {
  231. const { distribution_bucket_family_id: familyId, distribution_bucket_index: bucketIndex } = runtimeBucketId
  232. return distributionBucketIdByFamilyAndIndex(familyId, bucketIndex)
  233. }
  234. export function distributionBucketIdByFamilyAndIndex(
  235. familyId: DistributionBucketFamilyId,
  236. bucketIndex: DistributionBucketIndex
  237. ): string {
  238. return `${familyId.toString()}:${bucketIndex.toString()}`
  239. }
  240. export function distributionOperatorId(bucketId: DistributionBucketId, workerId: WorkerId): string {
  241. return `${distributionBucketId(bucketId)}-${workerId.toString()}`
  242. }