123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- 'use strict'
- const debug = require('debug')('joystream:runtime:assets')
- const { decodeAddress } = require('@polkadot/keyring')
- const { StorageObjectOwner, DataObject } = require('@joystream/types/storage')
- function parseContentId(contentId) {
- try {
- return decodeAddress(contentId)
- } catch (err) {
- return contentId
- }
- }
- /*
- * Add asset related functionality to the substrate API.
- */
- class AssetsApi {
- static async create(base) {
- const ret = new AssetsApi()
- ret.base = base
- await AssetsApi.init()
- return ret
- }
- static async init() {
- debug('Init')
- }
- /*
- * Create and return a data object.
- */
- async createDataObject(accountId, memberId, contentId, doTypeId, size, ipfsCid) {
- contentId = parseContentId(contentId)
- const owner = {
- Member: memberId,
- }
- const content = [
- {
- content_id: contentId,
- type_id: doTypeId,
- size,
- ipfs_content_id: ipfsCid,
- },
- ]
- const tx = this.base.api.tx.dataDirectory.addContent(owner, content)
- await this.base.signAndSend(accountId, tx)
- // If the data object constructed properly, we should now be able to return
- // the data object from the state.
- return this.getDataObject(contentId)
- }
- /*
- * Returns the Data Object for a contendId.
- * Returns null if it doesn't exist.
- */
- async getDataObject(contentId) {
- contentId = parseContentId(contentId)
- // check if contentId key exists in map
- const storageSize = await this.base.api.query.dataDirectory.dataByContentId.size(contentId)
- if (storageSize.eq(0)) {
- return null
- }
- return this.base.api.query.dataDirectory.dataByContentId(contentId)
- }
- /*
- * Verify the liaison state for a DataObject:
- * - Check the content ID has a DataObject
- * - Check the storageProviderId is the liaison
- * - Check the liaison state is Pending
- *
- * Each failure errors out, success returns the data object.
- */
- async checkLiaisonForDataObject(storageProviderId, contentId) {
- contentId = parseContentId(contentId)
- const obj = await this.getDataObject(contentId)
- if (!obj) {
- throw new Error(`No DataObject found for content ID: ${contentId}`)
- }
- if (!obj.liaison.eq(storageProviderId)) {
- throw new Error(`This storage node is not liaison for the content ID: ${contentId}`)
- }
- if (obj.liaison_judgement.type !== 'Pending') {
- throw new Error(`Content upload has already been processed.`)
- }
- return obj
- }
- /*
- * Sets the data object liaison judgement to Accepted
- */
- async acceptContent(providerAccoundId, storageProviderId, contentId) {
- contentId = parseContentId(contentId)
- const tx = this.base.api.tx.dataDirectory.acceptContent(storageProviderId, contentId)
- return this.base.signAndSend(providerAccoundId, tx)
- }
- /*
- * Gets storage relationship for contentId for the given provider
- */
- async getStorageRelationshipAndId(storageProviderId, contentId) {
- contentId = parseContentId(contentId)
- const rids = await this.base.api.query.dataObjectStorageRegistry.relationshipsByContentId(contentId)
- while (rids.length) {
- const relationshipId = rids.shift()
- let relationship = await this.base.api.query.dataObjectStorageRegistry.relationships(relationshipId)
- relationship = relationship.unwrap()
- if (relationship.storage_provider.eq(storageProviderId)) {
- return { relationship, relationshipId }
- }
- }
- return {}
- }
- /*
- * Creates storage relationship for a data object and provider and
- * returns the relationship id
- */
- async createStorageRelationship(providerAccountId, storageProviderId, contentId) {
- contentId = parseContentId(contentId)
- const tx = this.base.api.tx.dataObjectStorageRegistry.addRelationship(storageProviderId, contentId)
- return this.base.signAndSendThenGetEventResult(providerAccountId, tx, {
- module: 'dataObjectStorageRegistry',
- event: 'DataObjectStorageRelationshipAdded',
- type: 'DataObjectStorageRelationshipId',
- index: 0,
- })
- }
- /*
- * Set the ready state for a data object storage relationship to the new value
- */
- async toggleStorageRelationshipReady(providerAccountId, storageProviderId, dosrId, ready) {
- const tx = ready
- ? this.base.api.tx.dataObjectStorageRegistry.setRelationshipReady(storageProviderId, dosrId)
- : this.base.api.tx.dataObjectStorageRegistry.unsetRelationshipReady(storageProviderId, dosrId)
- return this.base.signAndSend(providerAccountId, tx)
- }
- /*
- * Returns array of all the content ids in storage
- */
- async getKnownContentIds() {
- const keys = await this.base.api.query.dataDirectory.dataByContentId.keys()
- return keys.map(({ args: [contentId] }) => contentId)
- }
- /*
- * Returns array of all content ids in storage where liaison judgement was Accepted
- */
- getAcceptedContentIds() {
- if (!this._cachedEntries) {
- return []
- }
- return this._cachedEntries
- .filter(([, dataObject]) => dataObject.liaison_judgement.type === 'Accepted')
- .map(
- ([
- {
- args: [contentId],
- },
- ]) => contentId
- )
- }
- /*
- * Returns array of all ipfs hashes in storage where liaison judgement was Accepted
- */
- getAcceptedIpfsHashes() {
- if (!this._cachedEntries) {
- return []
- }
- const hashes = new Map()
- this._cachedEntries
- .filter(([, dataObject]) => dataObject.liaison_judgement.type === 'Accepted')
- .forEach(([, dataObject]) => hashes.set(dataObject.ipfs_content_id.toString()))
- return Array.from(hashes.keys())
- }
- /*
- * Fetch and cache all data objects
- */
- async fetchDataObjects() {
- this._cachedEntries = await this.base.api.query.dataDirectory.dataByContentId.entries()
- this._idMappings = new Map()
- this._cachedEntries.forEach(([{ args: [contentId] }, dataObject]) =>
- this._idMappings.set(contentId.encode(), dataObject.ipfs_content_id.toString())
- )
- }
- resolveContentIdToIpfsHash(contentId) {
- if (!this._idMappings) return null
- return this._idMappings.get(contentId)
- }
- }
- module.exports = {
- AssetsApi,
- }
|