utils.ts 7.6 KB

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