Browse Source

storage-node: deal correctly with linked_map with non existent keys

Mokhtar Naamani 4 years ago
parent
commit
64a608b614
2 changed files with 44 additions and 24 deletions
  1. 4 4
      storage-node/packages/helios/bin/cli.js
  2. 40 20
      storage-node/packages/runtime-api/workers.js

+ 4 - 4
storage-node/packages/helios/bin/cli.js

@@ -15,7 +15,7 @@ async function main () {
   const currentHeight = currentHeader.number.toBn()
 
   // get all providers
-  const storageProviders = await runtime.workers.getAllProviders()
+  const { ids: storageProviders } = await runtime.workers.getAllProviders()
   console.log(`Found ${storageProviders.length} staked providers`)
 
   const storageProviderAccountInfos = await Promise.all(storageProviders.map(async (providerId) => {
@@ -105,7 +105,7 @@ async function main () {
   endpoints.forEach(async ({ providerId, endpoint }) => {
     if (!endpoint) { return }
     const total = knownContentIds.length
-    let { found, missing } = await countContentAvailability(knownContentIds, endpoint)
+    let { found } = await countContentAvailability(knownContentIds, endpoint)
     console.log(`provider ${providerId}: has ${found} out of ${total}`)
   })
 }
@@ -114,14 +114,14 @@ function mapInfoToStatus (providers, currentHeight) {
   return providers.map(({providerId, info}) => {
     if (info) {
       return {
-        providerId: providerId.toNumber(),
+        providerId,
         identity: info.identity.toString(),
         expiresIn: info.expires_at.sub(currentHeight).toNumber(),
         expired: currentHeight.gte(info.expires_at)
       }
     } else {
       return {
-        providerId: providerId.toNumber(),
+        providerId,
         identity: null,
         status: 'down'
       }

+ 40 - 20
storage-node/packages/runtime-api/workers.js

@@ -20,6 +20,8 @@
 
 const debug = require('debug')('joystream:runtime:roles')
 const BN = require('bn.js')
+// const { createType } = require('@polkadot/types')
+const { Worker } = require('@joystream/types/lib/working-group')
 
 /*
  * Add worker related functionality to the substrate API.
@@ -42,8 +44,8 @@ class WorkersApi {
   async isRoleAccountOfStorageProvider (storageProviderId, roleAccountId) {
     storageProviderId = new BN(storageProviderId)
     roleAccountId = this.base.identities.keyring.decodeAddress(roleAccountId)
-    const worker = await this.storageWorkerByProviderId(storageProviderId)
-    return worker && worker.role_account.eq(roleAccountId)
+    const account = await this.storageProviderRoleAccount(storageProviderId)
+    return account && account.eq(roleAccountId)
   }
 
   async isStorageProvider (storageProviderId) {
@@ -51,30 +53,48 @@ class WorkersApi {
     return worker !== null
   }
 
+  // Returns a provider's role account or null if provider doesn't exist
+  async storageProviderRoleAccount (storageProviderId) {
+    const worker = await this.storageWorkerByProviderId(storageProviderId)
+    return worker ? worker.role_account : null
+  }
+
+  // Returns a Worker instance or null if provider does not exist
   async storageWorkerByProviderId (storageProviderId) {
     storageProviderId = new BN(storageProviderId)
-    const nextWorkerId = await this.base.api.query.storageWorkingGroup.nextWorkerId()
-
-    if (storageProviderId.gte(nextWorkerId)) {
-      return null
-    }
-
-    const workerEntry = await this.base.api.query.storageWorkingGroup.workerById(storageProviderId)
-    return workerEntry[0]
+    const { providers } = await this.getAllProviders()
+    return providers[storageProviderId.toNumber()] || null
   }
 
-  async storageProviderRoleAccount (storageProviderId) {
-    const worker = await this.storageWorkerByProviderId(storageProviderId)
-    if (worker == null) {
-      throw new Error('Storage Provider Does Not Exist')
-    } else {
-      return worker.role_account
+  async getAllProviders () {
+    // const workerEntries = await this.base.api.query.storageWorkingGroup.workerById()
+    // can't rely on .isEmpty or isNone property to detect empty map
+    // return workerEntries.isNone ? [] : workerEntries[0]
+    // return workerEntries.isEmpty ? [] : workerEntries[0]
+    // So we iterate over possible ids which may or may not exist, by reading directly
+    // from storage value
+    const nextWorkerId = (await this.base.api.query.storageWorkingGroup.nextWorkerId()).toNumber()
+    let ids = []
+    let providers = {}
+    for (let id = 0; id < nextWorkerId; id++) {
+      // We get back an Option. Will be None if value doesn't exist
+      let value = await this.base.api.rpc.state.getStorage(
+        this.base.api.query.storageWorkingGroup.workerById.key(id)
+      )
+
+      if (!value.isNone) {
+        // no need to read from storage again!
+        // const worker = (await this.base.api.query.storageWorkingGroup.workerById(id))[0]
+        value = value.unwrap()
+        // construct the Worker type from raw data
+        // const worker = createType('WorkerOf', value)
+        // const worker = new Worker(value)
+        ids.push(id)
+        providers[id] = new Worker(value)
+      }
     }
-  }
 
-  async getAllProviders () {
-    const workerEntries = await this.base.api.query.storageWorkingGroup.workerById()
-    return workerEntries[0] // keys
+    return { ids, providers }
   }
 }