mirror of
https://github.com/oceanprotocol/ocean.js.git
synced 2024-11-26 20:39:05 +01:00
424 lines
14 KiB
TypeScript
424 lines
14 KiB
TypeScript
import { SearchQuery } from '../metadatastore/MetadataStore'
|
|
import { DDO } from '../ddo/DDO'
|
|
import { Metadata } from '../ddo/interfaces/Metadata'
|
|
import {
|
|
Service,
|
|
ServiceAccess,
|
|
ServiceComputePrivacy,
|
|
ServiceCommon
|
|
} from '../ddo/interfaces/Service'
|
|
import { EditableMetadata } from '../ddo/interfaces/EditableMetadata'
|
|
import Account from './Account'
|
|
import DID from './DID'
|
|
import { SubscribablePromise } from '../utils'
|
|
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
|
import { WebServiceConnector } from './utils/WebServiceConnector'
|
|
|
|
export enum CreateProgressStep {
|
|
CreatingDataToken,
|
|
DataTokenCreated,
|
|
EncryptingFiles,
|
|
FilesEncrypted,
|
|
GeneratingProof,
|
|
ProofGenerated,
|
|
StoringDdo,
|
|
DdoStored
|
|
}
|
|
|
|
/**
|
|
* Assets submodule of Ocean Protocol.
|
|
*/
|
|
export class Assets extends Instantiable {
|
|
/**
|
|
* Returns the instance of Assets.
|
|
* @return {Promise<Assets>}
|
|
*/
|
|
public static async getInstance(config: InstantiableConfig): Promise<Assets> {
|
|
const instance = new Assets()
|
|
instance.setInstanceConfig(config)
|
|
|
|
return instance
|
|
}
|
|
|
|
/**
|
|
* Creates a simple asset and a datatoken
|
|
* @param {Account} publisher Publisher account.
|
|
* @return {Promise<String>}
|
|
*/
|
|
public createSimpleAsset(publisher: Account): Promise<string> {
|
|
const publisherURI = this.ocean.provider.getURI()
|
|
const jsonBlob = { t: 0, url: publisherURI }
|
|
const { datatokens } = this.ocean
|
|
return datatokens.create(JSON.stringify(jsonBlob), publisher)
|
|
}
|
|
|
|
/**
|
|
* Creates a new DDO and publishes it
|
|
* @param {Metadata} metadata DDO metadata.
|
|
* @param {Account} publisher Publisher account.
|
|
* @param {list} services list of Service description documents
|
|
* @return {Promise<DDO>}
|
|
*/
|
|
public create(
|
|
metadata: Metadata,
|
|
publisher: Account,
|
|
services: Service[] = [],
|
|
dtAddress?: string
|
|
): SubscribablePromise<CreateProgressStep, DDO> {
|
|
this.logger.log('Creating asset')
|
|
return new SubscribablePromise(async (observer) => {
|
|
if (services.length === 0) {
|
|
this.logger.log('You have no services. Are you sure about this?')
|
|
}
|
|
if (!dtAddress) {
|
|
this.logger.log('Creating datatoken')
|
|
observer.next(CreateProgressStep.CreatingDataToken)
|
|
const metadataStoreURI = this.ocean.metadatastore.getURI()
|
|
const jsonBlob = { t: 1, url: metadataStoreURI }
|
|
const { datatokens } = this.ocean
|
|
dtAddress = await datatokens.create(JSON.stringify(jsonBlob), publisher)
|
|
this.logger.log('DataToken creted')
|
|
observer.next(CreateProgressStep.DataTokenCreated)
|
|
}
|
|
|
|
const did: DID = DID.generate()
|
|
|
|
this.logger.log('Encrypting files')
|
|
observer.next(CreateProgressStep.EncryptingFiles)
|
|
const encryptedFiles = await this.ocean.provider.encrypt(
|
|
did.getId(),
|
|
metadata.main.files,
|
|
publisher
|
|
)
|
|
this.logger.log('Files encrypted')
|
|
observer.next(CreateProgressStep.FilesEncrypted)
|
|
|
|
let indexCount = 0
|
|
// create ddo itself
|
|
const ddo: DDO = new DDO({
|
|
id: did.getDid(),
|
|
dataToken: dtAddress,
|
|
authentication: [
|
|
{
|
|
type: 'RsaSignatureAuthentication2018',
|
|
publicKey: did.getDid()
|
|
}
|
|
],
|
|
publicKey: [
|
|
{
|
|
id: did.getDid(),
|
|
type: 'EthereumECDSAKey',
|
|
owner: publisher.getId()
|
|
}
|
|
],
|
|
service: [
|
|
{
|
|
type: 'metadata',
|
|
attributes: {
|
|
// Default values
|
|
curation: {
|
|
rating: 0,
|
|
numVotes: 0
|
|
},
|
|
// Overwrites defaults
|
|
...metadata,
|
|
encryptedFiles,
|
|
// Cleaning not needed information
|
|
main: {
|
|
...metadata.main,
|
|
files: metadata.main.files.map((file, index) => ({
|
|
...file,
|
|
index,
|
|
url: undefined
|
|
}))
|
|
} as any
|
|
}
|
|
},
|
|
...services
|
|
]
|
|
// Remove duplications
|
|
.reverse()
|
|
.filter(
|
|
({ type }, i, list) =>
|
|
list.findIndex(({ type: t }) => t === type) === i
|
|
)
|
|
.reverse()
|
|
// Adding index
|
|
.map((_) => ({
|
|
..._,
|
|
index: indexCount++
|
|
})) as Service[]
|
|
})
|
|
this.logger.log('Generating proof')
|
|
observer.next(CreateProgressStep.GeneratingProof)
|
|
await ddo.addProof(this.ocean, publisher.getId(), publisher.getPassword())
|
|
this.logger.log('Proof generated')
|
|
observer.next(CreateProgressStep.ProofGenerated)
|
|
this.logger.log('Storing DDO')
|
|
observer.next(CreateProgressStep.StoringDdo)
|
|
const storedDdo = await this.ocean.metadatastore.storeDDO(ddo)
|
|
this.logger.log('DDO stored')
|
|
observer.next(CreateProgressStep.DdoStored)
|
|
return storedDdo
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Returns the owner of an asset.
|
|
* @param {string} did Decentralized ID.
|
|
* @return {Promise<string>} Returns Account ID
|
|
*/
|
|
public async owner(did: string): Promise<string> {
|
|
// TODO:
|
|
// const owner = await this.ocean.keeper.didRegistry.getDIDOwner(did)
|
|
// return owner
|
|
return ''
|
|
}
|
|
|
|
/**
|
|
* Returns the assets of a owner.
|
|
* @param {string} owner Owner address.
|
|
* @return {Promise<string[]>} List of DIDs.
|
|
*/
|
|
public async ownerAssets(owner: string): Promise<string[]> {
|
|
// TODO:
|
|
// return this.ocean.keeper.didRegistry.getAttributesByOwner(owner)
|
|
return ['']
|
|
}
|
|
|
|
/**
|
|
* Returns a DDO by DID.
|
|
* @param {string} did Decentralized ID.
|
|
* @return {Promise<DDO>}
|
|
*/
|
|
public async resolve(did: string): Promise<DDO> {
|
|
return this.ocean.metadatastore.retrieveDDO(did)
|
|
}
|
|
|
|
public async resolveByDTAddress(
|
|
dtAddress: string,
|
|
offset?: number,
|
|
page?: number,
|
|
sort?: number
|
|
): Promise<DDO[]> {
|
|
const searchQuery = {
|
|
offset: offset || 100,
|
|
page: page || 1,
|
|
query: {
|
|
dtAddress: [dtAddress]
|
|
},
|
|
sort: {
|
|
value: sort || 1
|
|
},
|
|
text: dtAddress
|
|
} as SearchQuery
|
|
return (await this.ocean.metadatastore.queryMetadata(searchQuery)).results
|
|
}
|
|
|
|
/**
|
|
* Edit Metadata for a DDO.
|
|
* @param {did} string DID.
|
|
* @param {newMetadata} EditableMetadata Metadata fields & new values.
|
|
* @param {Account} account Ethereum account of owner to sign and prove the ownership.
|
|
* @return {Promise<string>}
|
|
*/
|
|
public async editMetadata(
|
|
did: string,
|
|
newMetadata: EditableMetadata,
|
|
account: Account
|
|
): Promise<string> {
|
|
const oldDdo = await this.ocean.metadatastore.retrieveDDO(did)
|
|
// get a signature
|
|
const signature = await this.ocean.utils.signature.signForAquarius(
|
|
oldDdo.updated,
|
|
account
|
|
)
|
|
let result = null
|
|
if (signature != null)
|
|
result = await this.ocean.metadatastore.editMetadata(
|
|
did,
|
|
newMetadata,
|
|
oldDdo.updated,
|
|
signature
|
|
)
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Update Compute Privacy
|
|
* @param {did} string DID.
|
|
* @param {number} serviceIndex Index of the compute service in the DDO
|
|
* @param {ServiceComputePrivacy} computePrivacy ComputePrivacy fields & new values.
|
|
* @param {Account} account Ethereum account of owner to sign and prove the ownership.
|
|
* @return {Promise<string>}
|
|
*/
|
|
public async updateComputePrivacy(
|
|
did: string,
|
|
serviceIndex: number,
|
|
computePrivacy: ServiceComputePrivacy,
|
|
account: Account
|
|
): Promise<string> {
|
|
const oldDdo = await this.ocean.metadatastore.retrieveDDO(did)
|
|
// get a signature
|
|
const signature = await this.ocean.utils.signature.signForAquarius(
|
|
oldDdo.updated,
|
|
account
|
|
)
|
|
let result = null
|
|
if (signature != null)
|
|
result = await this.ocean.metadatastore.updateComputePrivacy(
|
|
did,
|
|
serviceIndex,
|
|
computePrivacy.allowRawAlgorithm,
|
|
computePrivacy.allowNetworkAccess,
|
|
computePrivacy.trustedAlgorithms,
|
|
oldDdo.updated,
|
|
signature
|
|
)
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Retire a DDO (Delete)
|
|
* @param {did} string DID.
|
|
* @param {Account} account Ethereum account of owner to sign and prove the ownership.
|
|
* @return {Promise<string>}
|
|
*/
|
|
public async retire(did: string, account: Account): Promise<string> {
|
|
const oldDdo = await this.ocean.metadatastore.retrieveDDO(did)
|
|
// get a signature
|
|
const signature = await this.ocean.utils.signature.signForAquarius(
|
|
oldDdo.updated,
|
|
account
|
|
)
|
|
let result = null
|
|
if (signature != null)
|
|
result = await this.ocean.metadatastore.retire(did, oldDdo.updated, signature)
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Returns the creator of a asset.
|
|
* @param {string} did Decentralized ID.
|
|
* @return {Promise<string>} Returns eth address
|
|
*/
|
|
public async creator(did: string): Promise<string> {
|
|
const ddo = await this.resolve(did)
|
|
const checksum = ddo.getChecksum()
|
|
const { creator, signatureValue } = ddo.proof
|
|
const signer = await this.ocean.utils.signature.verifyText(
|
|
checksum,
|
|
signatureValue
|
|
)
|
|
|
|
if (signer.toLowerCase() !== creator.toLowerCase()) {
|
|
this.logger.warn(
|
|
`Owner of ${ddo.id} doesn't match. Expected ${creator} instead of ${signer}.`
|
|
)
|
|
}
|
|
|
|
return creator
|
|
}
|
|
|
|
/**
|
|
* Search over the assets using a query.
|
|
* @param {SearchQuery} query Query to filter the assets.
|
|
* @return {Promise<DDO[]>}
|
|
*/
|
|
public async query(query: SearchQuery) {
|
|
return this.ocean.metadatastore.queryMetadata(query)
|
|
}
|
|
|
|
/**
|
|
* Search over the assets using a keyword.
|
|
* @param {SearchQuery} text Text to filter the assets.
|
|
* @return {Promise<DDO[]>}
|
|
*/
|
|
public async search(text: string) {
|
|
return this.ocean.metadatastore.queryMetadataByText({
|
|
text,
|
|
page: 1,
|
|
offset: 100,
|
|
query: {
|
|
value: 1
|
|
},
|
|
sort: {
|
|
value: 1
|
|
}
|
|
} as SearchQuery)
|
|
}
|
|
|
|
public async getService(did: string, serviceType: string): Promise<ServiceCommon> {
|
|
const services: ServiceCommon[] = (await this.resolve(did)).service
|
|
let service
|
|
services.forEach((serv) => {
|
|
if (serv.type.toString() === serviceType) {
|
|
service = serv
|
|
}
|
|
})
|
|
return service
|
|
}
|
|
|
|
public async createAccessServiceAttributes(
|
|
creator: Account,
|
|
dtCost: number,
|
|
datePublished: string,
|
|
timeout: number = 0
|
|
): Promise<ServiceAccess> {
|
|
return {
|
|
type: 'access',
|
|
index: 2,
|
|
serviceEndpoint: this.ocean.provider.getConsumeEndpoint(),
|
|
attributes: {
|
|
main: {
|
|
creator: creator.getId(),
|
|
datePublished,
|
|
dtCost,
|
|
timeout: timeout,
|
|
name: 'dataAssetAccessServiceAgreement'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public async order(
|
|
did: string,
|
|
serviceType: string,
|
|
consumerAddress: string
|
|
): Promise<string> {
|
|
const service = await this.getService(did, serviceType)
|
|
return await this.ocean.provider.initialize(
|
|
did,
|
|
service.index,
|
|
serviceType,
|
|
consumerAddress
|
|
)
|
|
}
|
|
|
|
public async download(
|
|
dtAddress: string,
|
|
serviceEndpoint: string,
|
|
txId: string,
|
|
account: string
|
|
): Promise<string> {
|
|
let consumeUrl = serviceEndpoint
|
|
consumeUrl += `?consumerAddress=${account}`
|
|
consumeUrl += `&tokenAddress=${dtAddress}`
|
|
consumeUrl += `&transferTxId=${txId}`
|
|
console.log(consumeUrl)
|
|
const serviceConnector = new WebServiceConnector(this.logger)
|
|
|
|
try {
|
|
await serviceConnector.downloadFile(consumeUrl)
|
|
} catch (e) {
|
|
this.logger.error('Error consuming assets')
|
|
this.logger.error(e)
|
|
throw e
|
|
}
|
|
|
|
return serviceEndpoint
|
|
}
|
|
}
|