extrinsics.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. import { sendAndFollowSudoNamedTx, sendAndFollowNamedTx, getEvent } from './api'
  2. import { getAlicePair } from './accounts'
  3. import { KeyringPair } from '@polkadot/keyring/types'
  4. import { ApiPromise } from '@polkadot/api'
  5. import { BagId, DynamicBagType } from '@joystream/types/storage'
  6. import logger from '../../services/logger'
  7. import { timeout } from 'promise-timeout'
  8. /**
  9. * Creates storage bucket.
  10. *
  11. * @remarks
  12. * It sends an extrinsic to the runtime.
  13. *
  14. * @param api - runtime API promise
  15. * @param account - KeyringPair instance
  16. * @param invitedWorker - defines whether the storage provider should be
  17. * invited on the bucket creation (optional)
  18. * @param allowedNewBags - bucket allows new bag assignments
  19. * @param sizeLimit - size limit in bytes for the new bucket (default 0)
  20. * @param objectsLimit - object number limit for the new bucket (default 0)
  21. * @returns promise with a success flag and the bucket id (on success).
  22. */
  23. export async function createStorageBucket(
  24. api: ApiPromise,
  25. account: KeyringPair,
  26. invitedWorker: number | null = null,
  27. allowedNewBags = true,
  28. sizeLimit = 0,
  29. objectsLimit = 0
  30. ): Promise<[boolean, number | void]> {
  31. let bucketId: number | void = 0
  32. const success = await extrinsicWrapper(async () => {
  33. const invitedWorkerValue = api.createType('Option<WorkerId>', invitedWorker)
  34. const tx = api.tx.storage.createStorageBucket(invitedWorkerValue, allowedNewBags, sizeLimit, objectsLimit)
  35. bucketId = await sendAndFollowNamedTx(api, account, tx, false, (result) => {
  36. const event = getEvent(result, 'storage', 'StorageBucketCreated')
  37. const bucketId = event?.data[0]
  38. return bucketId.toNumber()
  39. })
  40. })
  41. return [success, bucketId]
  42. }
  43. /**
  44. * Accepts the storage provider invitation for a bucket.
  45. *
  46. * @remarks
  47. * It sends an extrinsic to the runtime.
  48. *
  49. * @param api - runtime API promise
  50. * @param account - KeyringPair instance
  51. * @param workerId - runtime storage provider ID (worker ID)
  52. * @param storageBucketId - runtime storage bucket ID
  53. * @returns promise with a success flag.
  54. */
  55. export async function acceptStorageBucketInvitation(
  56. api: ApiPromise,
  57. account: KeyringPair,
  58. workerId: number,
  59. storageBucketId: number,
  60. transactorAccountId: string
  61. ): Promise<boolean> {
  62. return await extrinsicWrapper(() => {
  63. const tx = api.tx.storage.acceptStorageBucketInvitation(workerId, storageBucketId, transactorAccountId)
  64. return sendAndFollowNamedTx(api, account, tx)
  65. })
  66. }
  67. /**
  68. * Updates storage bucket to bags relationships.
  69. *
  70. * @remarks
  71. * It sends an extrinsic to the runtime.
  72. *
  73. * @param api - runtime API promise
  74. * @param bagId - BagId instance
  75. * @param account - KeyringPair instance
  76. * @param add - runtime storage bucket IDs to add
  77. * @param remove - runtime storage bucket IDs to remove
  78. * @returns promise with a success flag.
  79. */
  80. export async function updateStorageBucketsForBag(
  81. api: ApiPromise,
  82. bagId: BagId,
  83. account: KeyringPair,
  84. add: number[],
  85. remove: number[]
  86. ): Promise<boolean> {
  87. return await extrinsicWrapper(() => {
  88. const removeBuckets = api.createType('StorageBucketIdSet', remove)
  89. const addBuckets = api.createType('StorageBucketIdSet', add)
  90. const tx = api.tx.storage.updateStorageBucketsForBag(bagId, addBuckets, removeBuckets)
  91. return sendAndFollowNamedTx(api, account, tx)
  92. })
  93. }
  94. /**
  95. * Uploads a data object info.
  96. *
  97. * @remarks
  98. * It sends an extrinsic to the runtime.
  99. *
  100. * @param api - runtime API promise
  101. * @param account - KeyringPair instance
  102. * @param objectSize - object size in bytes
  103. * @param objectCid - object CID (Content ID - multihash)
  104. * @param dataFee - expected 'DataObjectPerMegabyteFee' runtime value
  105. * @returns promise with a success flag.
  106. */
  107. export async function uploadDataObjects(
  108. api: ApiPromise,
  109. objectSize: number,
  110. objectCid: string,
  111. dataFee: number
  112. ): Promise<boolean> {
  113. return await extrinsicWrapper(() => {
  114. const alice = getAlicePair()
  115. const data = api.createType('UploadParameters', {
  116. deletionPrizeSourceAccountId: alice.address,
  117. objectCreationList: [
  118. {
  119. Size: objectSize,
  120. IpfsContentId: objectCid,
  121. },
  122. ],
  123. expectedDataSizeFee: dataFee,
  124. })
  125. const tx = api.tx.storage.sudoUploadDataObjects(data)
  126. return sendAndFollowSudoNamedTx(api, alice, tx)
  127. })
  128. }
  129. /**
  130. * Accepts pending data objects by storage provider.
  131. *
  132. * @remarks
  133. * It sends an extrinsic to the runtime.
  134. *
  135. * @param api - runtime API promise
  136. * @param bagId - BagId instance
  137. * @param account - KeyringPair instance
  138. * @param workerId - runtime storage provider ID (worker ID)
  139. * @param storageBucketId - runtime storage bucket ID
  140. * @param dataObjects - runtime data objects IDs
  141. * @returns promise with a success flag.
  142. */
  143. export async function acceptPendingDataObjects(
  144. api: ApiPromise,
  145. bagId: BagId,
  146. account: KeyringPair,
  147. workerId: number,
  148. storageBucketId: number,
  149. dataObjects: number[]
  150. ): Promise<boolean> {
  151. return await extrinsicWrapper(() => {
  152. const dataObjectSet = api.createType('DataObjectIdSet', dataObjects)
  153. const tx = api.tx.storage.acceptPendingDataObjects(workerId, storageBucketId, bagId, dataObjectSet)
  154. return sendAndFollowNamedTx(api, account, tx)
  155. }, true)
  156. }
  157. /**
  158. * Updates a 'StorageBucketsPerBag' limit.
  159. *
  160. * @remarks
  161. * It sends an extrinsic to the runtime.
  162. *
  163. * @param api - runtime API promise
  164. * @param bagId - BagId instance
  165. * @param newLimit - new limit
  166. * @returns promise with a success flag.
  167. */
  168. export async function updateStorageBucketsPerBagLimit(
  169. api: ApiPromise,
  170. account: KeyringPair,
  171. newLimit: number
  172. ): Promise<boolean> {
  173. return await extrinsicWrapper(() => {
  174. const tx = api.tx.storage.updateStorageBucketsPerBagLimit(newLimit)
  175. return sendAndFollowNamedTx(api, account, tx)
  176. })
  177. }
  178. /**
  179. * Updates a 'StorageBucketsVoucherMaxLimits' variables.
  180. *
  181. * @remarks
  182. * It sends an extrinsic to the runtime.
  183. *
  184. * @param api - runtime API promise
  185. * @param bagId - BagId instance
  186. * @param newSizeLimit - new size limit
  187. * @param newObjectLimit - new object limit
  188. * @returns promise with a success flag.
  189. */
  190. export async function updateStorageBucketsVoucherMaxLimits(
  191. api: ApiPromise,
  192. account: KeyringPair,
  193. newSizeLimit: number,
  194. newObjectLimit: number
  195. ): Promise<boolean> {
  196. return await extrinsicWrapper(() => {
  197. const tx = api.tx.storage.updateStorageBucketsVoucherMaxLimits(newSizeLimit, newObjectLimit)
  198. return sendAndFollowNamedTx(api, account, tx)
  199. })
  200. }
  201. /**
  202. * Deletes a storage bucket.
  203. *
  204. * @remarks
  205. * It sends an extrinsic to the runtime.
  206. *
  207. * @param api - runtime API promise
  208. * @param account - KeyringPair instance
  209. * @param bucketId - runtime storage bucket ID
  210. * @returns promise with a success flag.
  211. */
  212. export async function deleteStorageBucket(api: ApiPromise, account: KeyringPair, bucketId: number): Promise<boolean> {
  213. return await extrinsicWrapper(() => {
  214. const tx = api.tx.storage.deleteStorageBucket(bucketId)
  215. return sendAndFollowNamedTx(api, account, tx)
  216. })
  217. }
  218. /**
  219. * Invites a storage provider for a bucket.
  220. *
  221. * @remarks
  222. * It sends an extrinsic to the runtime.
  223. *
  224. * @param api - runtime API promise
  225. * @param account - KeyringPair instance
  226. * @param bucketId - runtime storage bucket ID
  227. * @param operatorId - runtime storage operator ID (worker ID)
  228. * @returns promise with a success flag.
  229. */
  230. export async function inviteStorageBucketOperator(
  231. api: ApiPromise,
  232. account: KeyringPair,
  233. bucketId: number,
  234. operatorId: number
  235. ): Promise<boolean> {
  236. return await extrinsicWrapper(() => {
  237. const tx = api.tx.storage.inviteStorageBucketOperator(bucketId, operatorId)
  238. return sendAndFollowNamedTx(api, account, tx)
  239. })
  240. }
  241. /**
  242. * Helper-function. It catches the error, logs it and returns a success flag.
  243. *
  244. * @param extrinsic - extrinsic promise
  245. * @param throwErr - disables the default error catch and rethrows an error
  246. * after logging.
  247. * @returns promise with a success flag.
  248. */
  249. async function extrinsicWrapper(
  250. extrinsic: () => Promise<void>,
  251. throwErr = false,
  252. timeoutMs = 25000 // 25s - default extrinsic timeout
  253. ): Promise<boolean> {
  254. try {
  255. await timeout(extrinsic(), timeoutMs)
  256. } catch (err) {
  257. logger.error(`Api Error: ${err}`)
  258. if (throwErr) {
  259. throw err
  260. }
  261. return false
  262. }
  263. return true
  264. }
  265. /**
  266. * Cancels the invite for the storage provider.
  267. *
  268. * @remarks
  269. * It sends an extrinsic to the runtime.
  270. *
  271. * @param api - runtime API promise
  272. * @param account - KeyringPair instance
  273. * @param bucketId - runtime storage bucket ID
  274. * @returns promise with a success flag.
  275. */
  276. export async function cancelStorageBucketOperatorInvite(
  277. api: ApiPromise,
  278. account: KeyringPair,
  279. bucketId: number
  280. ): Promise<boolean> {
  281. return await extrinsicWrapper(() => {
  282. const tx = api.tx.storage.cancelStorageBucketOperatorInvite(bucketId)
  283. return sendAndFollowNamedTx(api, account, tx)
  284. })
  285. }
  286. /**
  287. * Removes a storage provider association with a bucket.
  288. *
  289. * @remarks
  290. * It sends an extrinsic to the runtime.
  291. *
  292. * @param api - runtime API promise
  293. * @param account - KeyringPair instance
  294. * @param bucketId - runtime storage bucket ID
  295. * @returns promise with a success flag.
  296. */
  297. export async function removeStorageBucketOperator(
  298. api: ApiPromise,
  299. account: KeyringPair,
  300. bucketId: number
  301. ): Promise<boolean> {
  302. return await extrinsicWrapper(() => {
  303. const tx = api.tx.storage.removeStorageBucketOperator(bucketId)
  304. return sendAndFollowNamedTx(api, account, tx)
  305. })
  306. }
  307. /**
  308. * Updates a 'DataSizeFee' variable.
  309. *
  310. * @remarks
  311. * It sends an extrinsic to the runtime.
  312. *
  313. * @param api - runtime API promise
  314. * @param bagId - BagId instance
  315. * @param fee - new fee
  316. * @returns promise with a success flag.
  317. */
  318. export async function updateDataSizeFee(api: ApiPromise, account: KeyringPair, fee: number): Promise<boolean> {
  319. return await extrinsicWrapper(() => {
  320. const tx = api.tx.storage.updateDataSizeFee(fee)
  321. return sendAndFollowNamedTx(api, account, tx)
  322. })
  323. }
  324. /**
  325. * Sets a metadata for the storage provider.
  326. *
  327. * @remarks
  328. * It sends an extrinsic to the runtime.
  329. *
  330. * @param api - runtime API promise
  331. * @param account - KeyringPair instance
  332. * @param operatorId - runtime storage operator ID (worker ID)
  333. * @param bucketId - runtime storage bucket ID
  334. * @param metadata - new metadata
  335. * @returns promise with a success flag.
  336. */
  337. export async function setStorageOperatorMetadata(
  338. api: ApiPromise,
  339. account: KeyringPair,
  340. operatorId: number,
  341. bucketId: number,
  342. metadata: string
  343. ): Promise<boolean> {
  344. return await extrinsicWrapper(() => {
  345. const tx = api.tx.storage.setStorageOperatorMetadata(operatorId, bucketId, metadata)
  346. return sendAndFollowNamedTx(api, account, tx)
  347. })
  348. }
  349. /**
  350. * Updates the 'UploadingBlocked' variable.
  351. *
  352. * @remarks
  353. * It sends an extrinsic to the runtime.
  354. *
  355. * @param api - runtime API promise
  356. * @param account - KeyringPair instance
  357. * @param newStatus - new block status
  358. * @returns promise with a success flag.
  359. */
  360. export async function updateUploadingBlockedStatus(
  361. api: ApiPromise,
  362. account: KeyringPair,
  363. newStatus: boolean
  364. ): Promise<boolean> {
  365. return await extrinsicWrapper(() => {
  366. const tx = api.tx.storage.updateUploadingBlockedStatus(newStatus)
  367. return sendAndFollowNamedTx(api, account, tx)
  368. })
  369. }
  370. /**
  371. * Updates the storage bucket status (whether it accepts new bags).
  372. *
  373. * @remarks
  374. * It sends an extrinsic to the runtime.
  375. *
  376. * @param api - runtime API promise
  377. * @param account - KeyringPair instance
  378. * @param storageBucketId - runtime storage bucket ID
  379. * @param newStatus - new storage bucket status status (accepts new bag)
  380. * @returns promise with a success flag.
  381. */
  382. export async function updateStorageBucketStatus(
  383. api: ApiPromise,
  384. account: KeyringPair,
  385. storageBucketId: number,
  386. newStatus: boolean
  387. ): Promise<boolean> {
  388. return await extrinsicWrapper(() => {
  389. const tx = api.tx.storage.updateStorageBucketStatus(storageBucketId, newStatus)
  390. return sendAndFollowNamedTx(api, account, tx)
  391. })
  392. }
  393. /**
  394. * Updates a 'StorageBucketVoucherLimits' variable for a storage bucket.
  395. *
  396. * @remarks
  397. * It sends an extrinsic to the runtime.
  398. *
  399. * @param api - runtime API promise
  400. * @param account - KeyringPair instance
  401. * @param workerId - runtime storage provider ID (worker ID)
  402. * @param storageBucketId - runtime storage bucket ID
  403. * @param newSizeLimit - new size limit
  404. * @param newObjectLimit - new object limit
  405. * @returns promise with a success flag.
  406. */
  407. export async function setStorageBucketVoucherLimits(
  408. api: ApiPromise,
  409. account: KeyringPair,
  410. storageBucketId: number,
  411. newSizeLimit: number,
  412. newObjectLimit: number
  413. ): Promise<boolean> {
  414. return await extrinsicWrapper(() => {
  415. const tx = api.tx.storage.setStorageBucketVoucherLimits(storageBucketId, newSizeLimit, newObjectLimit)
  416. return sendAndFollowNamedTx(api, account, tx)
  417. })
  418. }
  419. /**
  420. * Updates the number of storage buckets in the dynamic bag creation policy.
  421. *
  422. * @remarks
  423. * It sends an extrinsic to the runtime.
  424. *
  425. * @param api - runtime API promise
  426. * @param account - KeyringPair instance
  427. * @param dynamicBagType - dynamic bag type
  428. * @param newNumber - new number
  429. * @returns promise with a success flag.
  430. */
  431. export async function updateNumberOfStorageBucketsInDynamicBagCreationPolicy(
  432. api: ApiPromise,
  433. account: KeyringPair,
  434. dynamicBagType: DynamicBagType,
  435. newNumber: number
  436. ): Promise<boolean> {
  437. return await extrinsicWrapper(() => {
  438. const tx = api.tx.storage.updateNumberOfStorageBucketsInDynamicBagCreationPolicy(dynamicBagType, newNumber)
  439. return sendAndFollowNamedTx(api, account, tx)
  440. })
  441. }
  442. /**
  443. * Updates the blacklist for the storage.
  444. *
  445. * @remarks
  446. * It sends an extrinsic to the runtime.
  447. *
  448. * @param api - runtime API promise
  449. * @param account - KeyringPair instance
  450. * @param add - content IDs (multihash) to add
  451. * @param remove - content IDs (multihash) to add
  452. * @returns promise with a success flag.
  453. */
  454. export async function updateBlacklist(
  455. api: ApiPromise,
  456. account: KeyringPair,
  457. add: string[],
  458. remove: string[]
  459. ): Promise<boolean> {
  460. return await extrinsicWrapper(() => {
  461. const removeHashes = api.createType('ContentIdSet', remove)
  462. const addHashes = api.createType('ContentIdSet', add)
  463. const tx = api.tx.storage.updateBlacklist(removeHashes, addHashes)
  464. return sendAndFollowNamedTx(api, account, tx)
  465. })
  466. }