import { Instantiable, InstantiableConfig } from '../Instantiable.abstract' import { MetaData, MetaDataAlgorithm } from '../ddo/MetaData' import Account from './Account' import { DDO } from '../ddo/DDO' import { SubscribablePromise } from '../utils' import { OrderProgressStep } from './utils/ServiceUtils' import { DID } from '../squid' import { Service, ServiceCompute, ServiceComputePrivacy } from '../ddo/Service' export const ComputeJobStatus = Object.freeze({ Started: 10, ConfiguringVolumes: 20, ProvisioningSuccess: 30, DataProvisioningFailed: 31, AlgorithmProvisioningFailed: 32, RunningAlgorithm: 40, FilteringResults: 50, PublishingResult: 60, Completed: 70, Stopped: 80, Deleted: 90 }) export interface Output { publishAlgorithmLog?: boolean publishOutput?: boolean brizoAddress?: string brizoUri?: string metadata?: MetaData metadataUri?: string nodeUri?: string owner?: string secretStoreUri?: string whitelist?: string[] } export interface ComputeJob { owner: string agreementId: string jobId: string dateCreated: string dateFinished: string status: number statusText: string algorithmLogUrl: string resultsUrls: string[] resultsDid?: DID } /** * Compute submodule of Ocean Protocol. */ export class OceanCompute extends Instantiable { /** * Returns the instance of OceanCompute. * @return {Promise} */ public static async getInstance(config: InstantiableConfig): Promise { const instance = new OceanCompute() instance.setInstanceConfig(config) return instance } /** * Starts an order of a compute service that is defined in an asset's services. * @param {Account} consumerAccount The account of the consumer ordering the service. * @param {string} datasetDid The DID of the dataset asset (of type `dataset`) to run the algorithm on. * @param {string} algorithmDid The DID of the algorithm asset (of type `algorithm`) to run on the asset. * @param {MetaData} algorithmMeta Metadata about the algorithm being run if `algorithm` is being used. This is ignored when `algorithmDid` is specified. * @return {Promise} Returns the Service Agreement ID, representation of `bytes32` ID. * * Note: algorithmDid and algorithmMeta are optional, but if they are not passed, * you can end up in the situation that you are ordering and paying for your agreement, * but brizo will not allow the compute, due to privacy settings of the ddo */ public order( consumerAccount: Account, datasetDid: string, algorithmDid?: string, algorithmMeta?: MetaDataAlgorithm, provider?: string ): SubscribablePromise { return new SubscribablePromise(async (observer) => { const { assets, keeper, utils } = this.ocean const ddo: DDO = await assets.resolve(datasetDid) const service: Service = ddo.findServiceByType('compute') if (!service) return null if (algorithmMeta) { // check if raw algo is allowed if (service.attributes.main.privacy) if (!service.attributes.main.privacy.allowRawAlgorithm) return null } if (algorithmDid) { // check if did is in trusted list if (service.attributes.main.privacy) if (service.attributes.main.privacy.trustedAlgorithms) if (service.attributes.main.privacy.trustedAlgorithms.length > 0) if ( !service.attributes.main.privacy.trustedAlgorithms.includes( algorithmDid ) ) return null } const condition = keeper.conditions.computeExecutionCondition const agreementId = await utils.services.order( 'compute', condition, observer, consumerAccount, ddo, provider ) return agreementId }) } /** * Check the output object and add default properties if needed * @param {Account} consumerAccount The account of the consumer ordering the service. * @param {Output} output Output section used for publishing the result. * @return {Promise} Returns output object */ public checkOutput(consumerAccount: Account, output?: Output): Output { const isDefault = !output || (!output.publishAlgorithmLog && !output.publishOutput) if (isDefault) { return { publishAlgorithmLog: false, publishOutput: false } } return { publishAlgorithmLog: output.publishAlgorithmLog, publishOutput: output.publishOutput, brizoAddress: output.brizoAddress || this.config.brizoAddress, brizoUri: output.brizoUri || this.config.brizoUri, metadataUri: output.metadataUri || this.config.aquariusUri, nodeUri: output.nodeUri || this.config.nodeUri, owner: output.owner || consumerAccount.getId(), secretStoreUri: output.secretStoreUri || this.config.secretStoreUri } } /** * Start the execution of a compute job. * @param {Account} consumerAccount The account of the consumer ordering the service. * @param {string} agreementId The service agreement ID. * @param {string} algorithmDid The DID of the algorithm asset (of type `algorithm`) to run on the asset. * @param {MetaData} algorithmMeta Metadata about the algorithm being run if `algorithm` is being used. This is ignored when `algorithmDid` is specified. * @param {Output} output Define algorithm output publishing. Publishing the result of a compute job is turned off by default. * @return {Promise} Returns compute job ID under status.jobId */ public async start( consumerAccount: Account, agreementId: string, algorithmDid?: string, algorithmMeta?: MetaDataAlgorithm, output?: Output ): Promise { output = this.checkOutput(consumerAccount, output) if (agreementId) { const computeJobsList = await this.ocean.brizo.compute( 'post', agreementId, consumerAccount, algorithmDid, algorithmMeta, undefined, output ) return computeJobsList[0] as ComputeJob } else return null } /** * Ends a running compute job. * @param {Account} consumerAccount The account of the consumer ordering the service. * @param {string} agreementId The service agreement ID. * @param {string} jobId The ID of the compute job to be stopped * @return {Promise} Returns the new status of a job */ public async stop( consumerAccount: Account, agreementId: string, jobId: string ): Promise { const computeJobsList = await this.ocean.brizo.compute( 'put', agreementId, consumerAccount, undefined, undefined, jobId ) return computeJobsList[0] as ComputeJob } /** * Deletes a compute job and all resources associated with the job. If job is running it will be stopped first. * @param {Account} consumerAccount The account of the consumer ordering the service. * @param {string} agreementId The service agreement ID. * @param {string} jobId The ID of the compute job to be stopped * @return {Promise} Returns the new status of a job */ public async delete( consumerAccount: Account, agreementId: string, jobId: string ): Promise { const computeJobsList = await this.ocean.brizo.compute( 'delete', agreementId, consumerAccount, undefined, undefined, jobId ) return computeJobsList[0] as ComputeJob } /** * Ends a running compute job and starts it again. * @param {Account} consumerAccount The account of the consumer ordering the service. * @param {string} agreementId The service agreement ID. * @param {string} jobId The ID of the compute job to be stopped * @return {Promise} Returns the new status of a job */ public async restart( consumerAccount: Account, agreementId: string, jobId: string ): Promise { await this.stop(consumerAccount, agreementId, jobId) const result = await this.start(consumerAccount, agreementId, jobId) return result } /** * Returns information about the status of all compute jobs, or a single compute job. * @param {Account} consumerAccount The account of the consumer ordering the service. * @param {string} agreementId The service agreement ID. * @param {string} jobId The ID of the compute job to be stopped * @return {Promise} Returns the status */ public async status( consumerAccount: Account, agreementId?: string, jobId?: string ): Promise { const computeJobsList = await this.ocean.brizo.compute( 'get', agreementId, consumerAccount, undefined, undefined, jobId ) return computeJobsList as ComputeJob[] } /** * Returns the final result of a specific compute job published as an asset. * @param {Account} consumerAccount The account of the consumer ordering the service. * @param {string} agreementId The service agreement ID. * @param {string} jobId The ID of the compute job to be stopped. * @return {Promise} Returns the DDO of the result asset. */ public async result( consumerAccount: Account, agreementId: string, jobId: string ): Promise { const computeJobsList = await this.ocean.brizo.compute( 'get', agreementId, consumerAccount, undefined, undefined, jobId ) return computeJobsList[0] as ComputeJob } public async createComputeServiceAttributes( consumerAccount: Account, price: string, datePublished: string, computePrivacy?: ServiceComputePrivacy, timeout?: number ): Promise { const { templates } = this.ocean.keeper const serviceAgreementTemplate = await templates.escrowComputeExecutionTemplate.getServiceAgreementTemplate() const name = 'dataAssetComputingServiceAgreement' if (!timeout) timeout = 3600 const service = { type: 'compute', index: 3, serviceEndpoint: this.ocean.brizo.getComputeEndpoint(), templateId: templates.escrowComputeExecutionTemplate.getId(), attributes: { main: { creator: consumerAccount.getId(), datePublished, price, privacy: {}, timeout: timeout, name }, serviceAgreementTemplate } } if (computePrivacy) service.attributes.main.privacy = computePrivacy return service as ServiceCompute } }