workers.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * This file is part of the storage node for the Joystream project.
  3. * Copyright (C) 2019 Joystream Contributors
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. 'use strict'
  19. const debug = require('debug')('joystream:runtime:roles')
  20. const BN = require('bn.js')
  21. // const { createType } = require('@polkadot/types')
  22. const { Worker } = require('@joystream/types/lib/working-group')
  23. /*
  24. * Add worker related functionality to the substrate API.
  25. */
  26. class WorkersApi {
  27. static async create (base) {
  28. const ret = new WorkersApi()
  29. ret.base = base
  30. await ret.init()
  31. return ret
  32. }
  33. // eslint-disable-next-line class-methods-use-this, require-await
  34. async init () {
  35. debug('Init')
  36. }
  37. /*
  38. * Check whether the given account and id represent an active storage provider
  39. */
  40. async isRoleAccountOfStorageProvider (storageProviderId, roleAccountId) {
  41. const id = new BN(storageProviderId)
  42. const roleAccount = this.base.identities.keyring.decodeAddress(roleAccountId)
  43. const providerAccount = await this.storageProviderRoleAccount(id)
  44. return providerAccount && providerAccount.eq(roleAccount)
  45. }
  46. async isStorageProvider (storageProviderId) {
  47. const worker = await this.storageWorkerByProviderId(storageProviderId)
  48. return worker !== null
  49. }
  50. // Returns a provider's role account or null if provider doesn't exist
  51. async storageProviderRoleAccount (storageProviderId) {
  52. const worker = await this.storageWorkerByProviderId(storageProviderId)
  53. return worker ? worker.role_account : null
  54. }
  55. // Returns a Worker instance or null if provider does not exist
  56. async storageWorkerByProviderId (storageProviderId) {
  57. const id = new BN(storageProviderId)
  58. const { providers } = await this.getAllProviders()
  59. return providers[id.toNumber()] || null
  60. }
  61. async findProviderIdByRoleAccount (roleAccount) {
  62. const { ids, providers } = await this.getAllProviders()
  63. for (let i = 0; i < ids.length; i++) {
  64. const id = ids[i]
  65. if (providers[id].role_account.eq(roleAccount)) {
  66. return id
  67. }
  68. }
  69. return null
  70. }
  71. async getAllProviders () {
  72. // const workerEntries = await this.base.api.query.storageWorkingGroup.workerById()
  73. // can't rely on .isEmpty or isNone property to detect empty map
  74. // return workerEntries.isNone ? [] : workerEntries[0]
  75. // return workerEntries.isEmpty ? [] : workerEntries[0]
  76. // So we iterate over possible ids which may or may not exist, by reading directly
  77. // from storage value
  78. const nextWorkerId = (await this.base.api.query.storageWorkingGroup.nextWorkerId()).toNumber()
  79. const ids = []
  80. const providers = {}
  81. for (let id = 0; id < nextWorkerId; id++) {
  82. // We get back an Option. Will be None if value doesn't exist
  83. // eslint-disable-next-line no-await-in-loop
  84. let value = await this.base.api.rpc.state.getStorage(
  85. this.base.api.query.storageWorkingGroup.workerById.key(id)
  86. )
  87. if (!value.isNone) {
  88. // no need to read from storage again!
  89. // const worker = (await this.base.api.query.storageWorkingGroup.workerById(id))[0]
  90. value = value.unwrap()
  91. // construct the Worker type from raw data
  92. // const worker = createType('WorkerOf', value)
  93. // const worker = new Worker(value)
  94. ids.push(id)
  95. providers[id] = new Worker(value)
  96. }
  97. }
  98. return { ids, providers }
  99. }
  100. // Helper methods below don't really belong in the colossus api, they are here
  101. // mainly to be used by the dev-init command in the cli to setup a development environment
  102. async dev_setLead(sudo, memberId, roleAccount) {
  103. const setLeadTx = this.base.api.tx.storageWorkingGroup.setLead(memberId, roleAccount)
  104. // make sudo call
  105. return this.base.signAndSend(
  106. sudo,
  107. this.base.api.tx.sudo.sudo(setLeadTx)
  108. )
  109. }
  110. async dev_addWorkerOpening(leadAccount) {
  111. const openTx = this.base.api.tx.storageWorkingGroup.addWorkerOpening('CurrentBlock', {
  112. application_rationing_policy: {
  113. 'max_active_applicants': 1
  114. },
  115. max_review_period_length: 1000
  116. // default values for everything else..
  117. }, 'dev-opening')
  118. const openingId = await this.base.signAndSendThenGetEventResult(leadAccount, openTx, {
  119. eventModule: 'storageWorkingGroup',
  120. eventName: 'WorkerOpeningAdded',
  121. eventProperty: 'WorkerOpeningId'
  122. })
  123. return openingId
  124. }
  125. async dev_applyOnOpening(openingId, memberId, memberAccount, roleAccount) {
  126. const applyTx = this.base.api.tx.storageWorkingGroup.applyOnWorkerOpening(
  127. memberId, openingId, roleAccount, null, null, `colossus-${memberId}`
  128. )
  129. const applicationId = await this.base.signAndSendThenGetEventResult(memberAccount, applyTx, {
  130. eventModule: 'storageWorkingGroup',
  131. eventName: 'AppliedOnWorkerOpening',
  132. eventProperty: 'WorkerApplicationId'
  133. })
  134. return applicationId
  135. }
  136. async dev_beginOpeningReview(openingId, leadAccount) {
  137. const reviewTx = this.base.api.tx.storageWorkingGroup.beginWorkerApplicantReview(openingId)
  138. return this.base.signAndSend(leadAccount, reviewTx)
  139. }
  140. async dev_fillOpeningWithSingleApplication(openingId, leadAccount, applicationId) {
  141. const fillTx = this.base.api.tx.storageWorkingGroup.fillWorkerOpening(openingId, [applicationId], null)
  142. const filledMap = await this.base.signAndSendThenGetEventResult(leadAccount, fillTx, {
  143. eventModule: 'storageWorkingGroup',
  144. eventName: 'WorkerOpeningFilled',
  145. eventProperty: 'WorkerApplicationIdToWorkerIdMap'
  146. })
  147. return filledMap
  148. }
  149. }
  150. module.exports = {
  151. WorkersApi
  152. }