diff --git a/ComputeExamples.md b/ComputeExamples.md index 1ebd3ec98..1baf3eaec 100644 --- a/ComputeExamples.md +++ b/ComputeExamples.md @@ -143,7 +143,8 @@ import { configHelperNetworks, ConfigHelper, getEventFromTx, - amountToUnits + amountToUnits, + isDefined } from '../../src/index.js' ``` import crypto from 'crypto-js' @@ -276,6 +277,9 @@ let resolvedAlgorithmDdo: DDO let computeJobId: string let agreementId: string + +let computeRoutePath: string +let hasFreeComputeSupport: boolean ``` ### 4.3 Helper methods @@ -595,7 +599,7 @@ Now, let's check that we successfully published a algorithm (create NFT + Datato let's check the free compute environment ```Typescript const computeEnv = computeEnvs[resolvedDatasetDdo.chainId].find( - (ce) => ce.priceMin === 0 + (ce) => ce.priceMin === 0 || isDefined(ce.free) ) console.log('Free compute environment = ', computeEnv) ``` @@ -603,111 +607,135 @@ let's check the free compute environment assert(computeEnv, 'Cannot find the free compute env') --> -Let's have 5 minute of compute access -```Typescript - const mytime = new Date() - const computeMinutes = 5 - mytime.setMinutes(mytime.getMinutes() + computeMinutes) - const computeValidUntil = Math.floor(mytime.getTime() / 1000) - - const assets: ComputeAsset[] = [ - { - documentId: resolvedDatasetDdo.id, - serviceId: resolvedDatasetDdo.services[0].id + computeRoutePath = await ProviderInstance.getComputeStartRoutes(providerUrl, true) + if (isDefined(computeRoutePath)) { + hasFreeComputeSupport = true + Let's have 5 minute of compute access + ```Typescript + const mytime = new Date() + const computeMinutes = 5 + mytime.setMinutes(mytime.getMinutes() + computeMinutes) + const computeValidUntil = Math.floor(mytime.getTime() / 1000) + + const assets: ComputeAsset[] = [ + { + documentId: resolvedDatasetDdo.id, + serviceId: resolvedDatasetDdo.services[0].id + } + ] + const dtAddressArray = [resolvedDatasetDdo.services[0].datatokenAddress] + const algo: ComputeAlgorithm = { + documentId: resolvedAlgorithmDdo.id, + serviceId: resolvedAlgorithmDdo.services[0].id } - ] - const dtAddressArray = [resolvedDatasetDdo.services[0].datatokenAddress] - const algo: ComputeAlgorithm = { - documentId: resolvedAlgorithmDdo.id, - serviceId: resolvedAlgorithmDdo.services[0].id - } - const providerInitializeComputeResults = await ProviderInstance.initializeCompute( - assets, - algo, - computeEnv.id, - computeValidUntil, - providerUrl, - await consumerAccount.getAddress() - ) -``` - -```Typescript - algo.transferTxId = await handleOrder( - providerInitializeComputeResults.algorithm, - resolvedAlgorithmDdo.services[0].datatokenAddress, - consumerAccount, - computeEnv.consumerAddress, - 0 - ) - for (let i = 0; i < providerInitializeComputeResults.datasets.length; i++) { - assets[i].transferTxId = await handleOrder( - providerInitializeComputeResults.datasets[i], - dtAddressArray[i], + const providerInitializeComputeResults = await ProviderInstance.initializeCompute( + assets, + algo, + computeEnv.id, + computeValidUntil, + providerUrl, + consumerAccount + ) + ``` + + ```Typescript + algo.transferTxId = await handleOrder( + providerInitializeComputeResults.algorithm, + resolvedAlgorithmDdo.services[0].datatokenAddress, consumerAccount, computeEnv.consumerAddress, 0 ) - } + for (let i = 0; i < providerInitializeComputeResults.datasets.length; i++) { + assets[i].transferTxId = await handleOrder( + providerInitializeComputeResults.datasets[i], + dtAddressArray[i], + consumerAccount, + computeEnv.consumerAddress, + 0 + ) + } - const computeJobs = await ProviderInstance.computeStart( - providerUrl, - consumerAccount, - computeEnv.id, - assets[0], - algo - ) + const computeJobs = await ProviderInstance.freeComputeStart( + providerUrl, + consumerAccount, + computeEnv.id, + assets, + algo + ) -``` - -Let's save the compute job it, we re going to use later -```Typescript - computeJobId = computeJobs[0].jobId - // eslint-disable-next-line prefer-destructuring - agreementId = computeJobs[0].agreementId + ``` + + Let's save the compute job it, we re going to use later + ```Typescript + computeJobId = computeJobs[0].jobId + // eslint-disable-next-line prefer-destructuring + agreementId = computeJobs[0].agreementId + } else { + assert( + computeRoutePath === null, + 'Route path for free compute is not defined (perhaps because provider does not support it yet?)' + ) + hasFreeComputeSupport = false + } ``` ## 11. Check compute status and get download compute results URL ### 11.1 Check compute status -You can also add various delays so you see the various states of the compute job -```Typescript - const jobStatus = await ProviderInstance.computeStatus( - providerUrl, - await consumerAccount.getAddress(), - computeJobId, - agreementId - ) -``` - -Now, let's see the current status of the previously started computer job -```Typescript - console.log('Current status of the compute job: ', jobStatus) + if (!hasFreeComputeSupport) { + assert( + computeRoutePath === null, + 'Compute route path for free compute is not defined (perhaps because provider does not support it yet?)' + ) + } else { + You can also add various delays so you see the various states of the compute job + ```Typescript + const jobStatus = await ProviderInstance.computeStatus( + providerUrl, + await consumerAccount.getAddress(), + computeJobId, + agreementId + ) + ``` + + Now, let's see the current status of the previously started computer job + ```Typescript + console.log('Current status of the compute job: ', jobStatus) + } ``` ### 11.2 Get download compute results URL -```Typescript - await sleep(10000) - const downloadURL = await ProviderInstance.getComputeResultUrl( - providerUrl, - consumerAccount, - computeJobId, - 0 - ) -``` - -Let's check the compute results url for the specified index -```Typescript - console.log(`Compute results URL: ${downloadURL}`) + if (!hasFreeComputeSupport) { + assert( + computeRoutePath === null, + 'Compute route path for free compute is not defined (perhaps because provider does not support it yet?)' + ) + } else { + ```Typescript + await sleep(10000) + const downloadURL = await ProviderInstance.getComputeResultUrl( + providerUrl, + consumerAccount, + computeJobId, + 0 + ) + ``` + + Let's check the compute results url for the specified index + ```Typescript + console.log(`Compute results URL: ${downloadURL}`) + } ``` diff --git a/docs/classes/Provider.md b/docs/classes/Provider.md index 9aee359ae..4b26ceeda 100644 --- a/docs/classes/Provider.md +++ b/docs/classes/Provider.md @@ -97,7 +97,7 @@ ___ ### computeStart -▸ **computeStart**(`providerUri`, `consumer`, `computeEnv`, `dataset`, `algorithm`, `signal?`, `additionalDatasets?`, `output?`): `Promise`<[`ComputeJob`](../interfaces/ComputeJob.md) \| [`ComputeJob`](../interfaces/ComputeJob.md)[]\> +▸ **computeStart**(`providerUri`, `signer`, `computeEnv`, `datasets`, `algorithm`, `resources`, `chainId`, `output?`, `freeEnvironment`, `signal?`): `Promise`<[`ComputeJob`](../interfaces/ComputeJob.md) \| [`ComputeJob`](../interfaces/ComputeJob.md)[]\> Instruct the provider to start a compute job @@ -106,13 +106,15 @@ Instruct the provider to start a compute job | Name | Type | Description | | :------ | :------ | :------ | | `providerUri` | `string` | The provider URI. | -| `consumer` | `Signer` | - | +| `signer` | `Signer` | - | The consumer signer/account | `computeEnv` | `string` | The compute environment. | -| `dataset` | [`ComputeAsset`](../interfaces/ComputeAsset.md) | The dataset to start compute on | +| `datasets` | [`ComputeAsset`](../interfaces/ComputeAsset.md) | The dataset to start compute on | | `algorithm` | [`ComputeAlgorithm`](../interfaces/ComputeAlgorithm.md) | The algorithm to start compute with. | -| `signal?` | `AbortSignal` | abort signal | -| `additionalDatasets?` | [`ComputeAsset`](../interfaces/ComputeAsset.md)[] | The additional datasets if that is the case. | +| `resources` | [`ComputeResourceRequest`](../interfaces/ComputeResourcesRequest.md) | The resources to start compute with. | +| `chainId?` | [`number`] | The network for the payments | | `output?` | [`ComputeOutput`](../interfaces/ComputeOutput.md) | The compute job output settings. | +| `signal?` | `AbortSignal` | abort signal | + #### Returns @@ -486,7 +488,7 @@ Initializes the provider for a compute request. | `computeEnv` | `string` | The compute environment. | | `validUntil` | `number` | The job expiration date. | | `providerUri` | `string` | The provider URI. | -| `accountId` | `string` | caller address | +| `signer` | `Signer` | caller account | | `signal?` | `AbortSignal` | abort signal | #### Returns @@ -501,6 +503,36 @@ ProviderComputeInitialize data ___ + +### computeStreamableLogs + +▸ **computeStreamableLogs**(`providerUri`, `signer`, `jobId`, `signal?`): `Promise`<`any`\> + +Gets the streamable compute logs. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `providerUri` | `string` | The provider URI. | +| `signer` | `Signer` | The signer. | +| `jobId` | `string` | The Job Id. | +| `signal?` | `AbortSignal` | The abort signal. | + + +#### Returns + +`Promise`<`any`\> + +The compute logs. + +#### Defined in + +[services/Provider.ts:908](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/services/Provider.ts#L908) + +___ + + ### inputMatch ▸ `Private` **inputMatch**(`input`, `regexp`, `conversorName`): `Object` diff --git a/docs/interfaces/ComputeEnvFees.md b/docs/interfaces/ComputeEnvFees.md new file mode 100644 index 000000000..af6c71ecc --- /dev/null +++ b/docs/interfaces/ComputeEnvFees.md @@ -0,0 +1,30 @@ +[@oceanprotocol/lib](../README.md) / [Exports](../modules.md) / ComputeEnvFees + +# Interface: ComputeEnvFees + +## Table of contents + +### Properties + +- [feeToken](ComputeEnvFees.md#feeToken) +- [prices](ComputeEnvFees.md#prices) + +## Properties + +### feeToken + +• **feeToken**: `string` + +#### Defined in + +[@types/Compute.ts:42](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L42) + +___ + +### prices + +• **prices**: `ComputeResourcesPricingInfo`[] + +#### Defined in + +[@types/Compute.ts:43](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L43) diff --git a/docs/interfaces/ComputeEnvFeesStructure.md b/docs/interfaces/ComputeEnvFeesStructure.md new file mode 100644 index 000000000..069dc662a --- /dev/null +++ b/docs/interfaces/ComputeEnvFeesStructure.md @@ -0,0 +1,20 @@ +[@oceanprotocol/lib](../README.md) / [Exports](../modules.md) / ComputeEnvFeesStructure + +# Interface: ComputeEnvFeesStructure + +## Table of contents + +### Properties + +- [feeToken](ComputeEnvFeesStructure.md#chainId) + +## Properties + +### chainId + +• **chainId**: `ComputeEnvFees` + +#### Defined in + +[@types/Compute.ts:42](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L46) + diff --git a/docs/interfaces/ComputeEnvironment.md b/docs/interfaces/ComputeEnvironment.md index 52997d032..a43ac78a0 100644 --- a/docs/interfaces/ComputeEnvironment.md +++ b/docs/interfaces/ComputeEnvironment.md @@ -7,20 +7,21 @@ ### Properties - [consumerAddress](ComputeEnvironment.md#consumeraddress) -- [cpuNumber](ComputeEnvironment.md#cpunumber) -- [cpuType](ComputeEnvironment.md#cputype) +- [totalCpu](ComputeEnvironment.md#totalCpu) +- [maxCpu](ComputeEnvironment.md#maxCpu) +- [totalRam](ComputeEnvironment.md#totalRam) +- [maxRam](ComputeEnvironment.md#maxRam) +- [maxDisk](ComputeEnvironment.md#maxDisk) - [currentJobs](ComputeEnvironment.md#currentjobs) -- [desc](ComputeEnvironment.md#desc) -- [diskGB](ComputeEnvironment.md#diskgb) -- [gpuNumber](ComputeEnvironment.md#gpunumber) -- [gpuType](ComputeEnvironment.md#gputype) +- [description](ComputeEnvironment.md#description) +- [fees](ComputeEnvironment.md#ComputeEnvFeesStructure) - [id](ComputeEnvironment.md#id) - [lastSeen](ComputeEnvironment.md#lastseen) - [maxJobDuration](ComputeEnvironment.md#maxjobduration) - [maxJobs](ComputeEnvironment.md#maxjobs) -- [priceMin](ComputeEnvironment.md#pricemin) -- [ramGB](ComputeEnvironment.md#ramgb) - [storageExpiry](ComputeEnvironment.md#storageexpiry) +- [lastSeen](ComputeEnvironment.md#lastSeen) +- [free](ComputeEnvironment.md#free) ## Properties @@ -30,27 +31,27 @@ #### Defined in -[@types/Compute.ts:21](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L21) +[@types/Compute.ts:68](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L68) ___ -### cpuNumber +### totalCpu -• **cpuNumber**: `number` +• **totalCpu**: `number` #### Defined in -[@types/Compute.ts:11](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L11) +[@types/Compute.ts:58](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L58) ___ -### cpuType +### maxCpu -• **cpuType**: `string` +• **maxCpu**: `number` #### Defined in -[@types/Compute.ts:12](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L12) +[@types/Compute.ts:59](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L59) ___ @@ -60,47 +61,47 @@ ___ #### Defined in -[@types/Compute.ts:19](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L19) +[@types/Compute.ts:66](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L66) ___ -### desc +### description -• **desc**: `string` +• **description**: `string` #### Defined in -[@types/Compute.ts:18](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L18) +[@types/Compute.ts:65](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L65) ___ -### diskGB +### maxDisk -• **diskGB**: `number` +• **maxDisk**: `number` #### Defined in -[@types/Compute.ts:16](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L16) +[@types/Compute.ts:62](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L62) ___ -### gpuNumber +### maxRam -• **gpuNumber**: `number` +• **maxRam**: `number` #### Defined in -[@types/Compute.ts:13](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L13) +[@types/Compute.ts:61](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L61) ___ -### gpuType +### totalRam -• **gpuType**: `string` +• **totalRam**: `number` #### Defined in -[@types/Compute.ts:14](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L14) +[@types/Compute.ts:60](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L60) ___ @@ -110,7 +111,7 @@ ___ #### Defined in -[@types/Compute.ts:10](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L10) +[@types/Compute.ts:49](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L49) ___ @@ -120,7 +121,7 @@ ___ #### Defined in -[@types/Compute.ts:24](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L24) +[@types/Compute.ts:71](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L71) ___ @@ -130,7 +131,7 @@ ___ #### Defined in -[@types/Compute.ts:23](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L23) +[@types/Compute.ts:70](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L70) ___ @@ -140,27 +141,27 @@ ___ #### Defined in -[@types/Compute.ts:20](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L20) +[@types/Compute.ts:67](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L67) ___ -### priceMin +### fees -• **priceMin**: `number` +• **fees**: `ComputeEnvFeesStructure` #### Defined in -[@types/Compute.ts:17](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L17) +[@types/Compute.ts:63](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L63) ___ -### ramGB +### free -• **ramGB**: `number` +• **free**: `boolean` #### Defined in -[@types/Compute.ts:15](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L15) +[@types/Compute.ts:72](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L72) ___ @@ -170,4 +171,14 @@ ___ #### Defined in -[@types/Compute.ts:22](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L22) +[@types/Compute.ts:69](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L69) + +___ + +### platform + +• **platform**: `RunningPlatform[]` + +#### Defined in + +[@types/Compute.ts:73](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L73) diff --git a/docs/interfaces/ComputeResourcesPricingInfo.md b/docs/interfaces/ComputeResourcesPricingInfo.md new file mode 100644 index 000000000..fcf6d6090 --- /dev/null +++ b/docs/interfaces/ComputeResourcesPricingInfo.md @@ -0,0 +1,30 @@ +[@oceanprotocol/lib](../README.md) / [Exports](../modules.md) / ComputeResourcesPricingInfo + +# Interface: ComputeResourcesPricingInfo + +## Table of contents + +### Properties + +- [type](ComputeResourcesPricingInfo.md#type) +- [price](ComputeResourcesPricingInfo.md#price) + +## Properties + +### type + +• **type**: `ComputeResourceType` + +#### Defined in + +[@types/Compute.ts:38](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L38) + +___ + +### price + +• **price**: `number` + +#### Defined in + +[@types/Compute.ts:39](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L39) diff --git a/docs/interfaces/ComputeResourcesRequest.md b/docs/interfaces/ComputeResourcesRequest.md new file mode 100644 index 000000000..67b7c166c --- /dev/null +++ b/docs/interfaces/ComputeResourcesRequest.md @@ -0,0 +1,30 @@ +[@oceanprotocol/lib](../README.md) / [Exports](../modules.md) / ComputeResourcesRequest + +# Interface: ComputeResourcesRequest + +## Table of contents + +### Properties + +- [id](ComputeResourcesRequest.md#id) +- [amount](ComputeResourcesRequest.md#amount) + +## Properties + +### id + +• **id**: `string` + +#### Defined in + +[@types/Compute.ts:63](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L63) + +___ + +### amount + +• **amount**: `number` + +#### Defined in + +[@types/Compute.ts:64](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L64) diff --git a/docs/interfaces/RunningPlatform.md b/docs/interfaces/RunningPlatform.md new file mode 100644 index 000000000..e6add7f78 --- /dev/null +++ b/docs/interfaces/RunningPlatform.md @@ -0,0 +1,30 @@ +[@oceanprotocol/lib](../README.md) / [Exports](../modules.md) / RunningPlatform + +# Interface: RunningPlatform + +## Table of contents + +### Properties + +- [architecture](RunningPlatform.md#architecture) +- [os](RunningPlatform.md#os) + +## Properties + +### architecture + +• **architecture**: `string` + +#### Defined in + +[@types/Compute.ts:31](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L31) + +___ + +### os + +• **os**: `string` + +#### Defined in + +[@types/Compute.ts:32](https://github.com/oceanprotocol/ocean.js/blob/c99bc5c6/src/@types/Compute.ts#L32) diff --git a/package-lock.json b/package-lock.json index 704c1762c..a32f5c221 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@oceanprotocol/lib", - "version": "3.4.6", + "version": "4.0.0-next.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@oceanprotocol/lib", - "version": "3.4.6", + "version": "4.0.0-next.1", "license": "Apache-2.0", "dependencies": { "@oasisprotocol/sapphire-paratime": "^1.3.2", @@ -23529,6 +23529,7 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + } } } diff --git a/package.json b/package.json index e780e39d6..31b140e6a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oceanprotocol/lib", "source": "./src/index.ts", - "version": "3.4.6", + "version": "4.0.0-next.1", "description": "JavaScript client library for Ocean Protocol", "main": "./dist/lib.cjs", "umd:main": "dist/lib.umd.js", diff --git a/src/@types/Compute.ts b/src/@types/Compute.ts index 81142c7cb..670a166bd 100644 --- a/src/@types/Compute.ts +++ b/src/@types/Compute.ts @@ -6,22 +6,98 @@ export type ComputeResultType = | 'configrationLog' | 'publishLog' +// OLD V1 ComputeEnvironment specs +// export interface ComputeEnvironment { +// id: string +// cpuNumber: number +// cpuType: string +// gpuNumber: number +// gpuType: string +// ramGB: number +// diskGB: number +// priceMin: number +// desc: string +// currentJobs: number +// maxJobs: number +// consumerAddress: string +// storageExpiry: number +// maxJobDuration: number +// lastSeen: number +// free: boolean +// } + +// new V2 C2D Compute Environment specs +export interface RunningPlatform { + architecture: string + os: string +} + +export type ComputeResourceType = 'cpu' | 'memory' | 'storage' + +export interface ComputeResourcesPricingInfo { + type: ComputeResourceType + price: number +} +export interface ComputeEnvFees { + feeToken: string + prices: ComputeResourcesPricingInfo[] +} +export interface ComputeEnvFeesStructure { + [chainId: string]: ComputeEnvFees +} + +export interface ComputeResourceRequest { + id: string + amount: number +} + +export interface ComputeResource { + id: ComputeResourceType + type?: string + kind?: string + total: number // total number of specific resource + min: number // min number of resource needed for a job + max: number // max number of resource for a job + inUse?: number // for display purposes +} + +export interface ComputeEnvironmentFreeOptions { + // only if a compute env exposes free jobs + storageExpiry?: number + maxJobDuration?: number + maxJobs?: number // maximum number of simultaneous free jobs + resources?: ComputeResource[] +} export interface ComputeEnvironment { + // legacy + // cpuNumber: number + // cpuType: string + // gpuNumber: number + // gpuType: string + // ramGB: number + // diskGB: number + // priceMin: number + // totalCpu: number // total cpu available for jobs + // maxCpu: number // max cpu for a single job. Imagine a K8 cluster with two nodes, each node with 10 cpus. Total=20, but at most you can allocate 10 cpu for a job + // totalRam: number // total gb of RAM + // maxRam: number // max allocatable GB RAM for a single job. + // maxDisk: number // max GB of disck allocatable for a single job + // currentJobs: number + // lastSeen: number + // legacy id: string - cpuNumber: number - cpuType: string - gpuNumber: number - gpuType: string - ramGB: number - diskGB: number - priceMin: number - desc: string - currentJobs: number - maxJobs: number + description: string consumerAddress: string - storageExpiry: number - maxJobDuration: number - lastSeen: number + storageExpiry?: number // amount of seconds for storage + minJobDuration?: number // min billable seconds for a paid job + maxJobDuration?: number // max duration in seconds for a paid job + maxJobs?: number // maximum number of simultaneous paid jobs + runningJobs: number // amount of running jobs (paid jobs) + runningfreeJobs?: number // amount of running jobs (free jobs) + fees: ComputeEnvFeesStructure + resources?: ComputeResource[] + free?: ComputeEnvironmentFreeOptions + platform?: RunningPlatform } export interface ComputeResult { @@ -59,7 +135,42 @@ export interface ComputeOutput { whitelist?: string[] } +export enum FileObjectType { + URL = 'url', + IPFS = 'ipfs', + ARWEAVE = 'arweave' +} + +export enum EncryptMethod { + AES = 'AES', + ECIES = 'ECIES' +} + +export interface HeadersObject { + [key: string]: string +} + +export interface BaseFileObject { + type: string + encryptedBy?: string + encryptMethod?: EncryptMethod +} + +export interface UrlFileObject extends BaseFileObject { + url: string + method: string + headers?: [HeadersObject] +} + +export interface IpfsFileObject extends BaseFileObject { + hash: string +} + +export interface ArweaveFileObject extends BaseFileObject { + transactionId: string +} export interface ComputeAsset { + fileObject?: BaseFileObject // C2D v2 documentId: string serviceId: string transferTxId?: string @@ -67,6 +178,7 @@ export interface ComputeAsset { } export interface ComputeAlgorithm { + fileObject?: BaseFileObject // C2D v2 documentId?: string serviceId?: string meta?: MetadataAlgorithm diff --git a/src/services/Provider.ts b/src/services/Provider.ts index f5f761b9a..5d996a211 100644 --- a/src/services/Provider.ts +++ b/src/services/Provider.ts @@ -16,7 +16,8 @@ import { UserCustomParameters, Ipfs, Smartcontract, - GraphqlQuery + GraphqlQuery, + ComputeResourceRequest } from '../@types' export class Provider { @@ -302,7 +303,7 @@ export class Provider { public async getComputeEnvironments( providerUri: string, signal?: AbortSignal - ): Promise<{ [chainId: number]: ComputeEnvironment[] }> { + ): Promise { const providerEndpoints = await this.getEndpoints(providerUri) const serviceEndpoints = await this.getServiceEndpoints( providerUri, @@ -324,10 +325,11 @@ export class Provider { } if (response?.ok) { const result = response.json() - if (Array.isArray(result)) { - const providerChain: number = providerEndpoints.chainId - return { [providerChain]: result } - } + // chain is not part of response + // if (Array.isArray(result)) { + // const providerChain: number = providerEndpoints.chainId + // return { [providerChain]: result } + // } return result } const resolvedResponse = await response.json() @@ -418,7 +420,7 @@ export class Provider { * @param {AbortSignal} signal abort signal * @return {Promise} ProviderComputeInitialize data */ - public async initializeCompute( + public async initializeComputeV1( assets: ComputeAsset[], algorithm: ComputeAlgorithm, computeEnv: string, @@ -470,6 +472,90 @@ export class Provider { throw new Error(JSON.stringify(resolvedResponse)) } + /** Initializes the provider for a compute request. + * @param {ComputeAsset[]} assets The datasets array to initialize compute request. + * @param {ComputeAlgorithmber} algorithm The algorithm to use. + * @param {string} computeEnv The compute environment. + * @param {number} validUntil The job expiration date. + * @param {string} providerUri The provider URI. + * @param {Signer} signer caller address + * @param {AbortSignal} signal abort signal + * @return {Promise} ProviderComputeInitialize data + */ + public async initializeCompute( + assets: ComputeAsset[], + algorithm: ComputeAlgorithm, + computeEnv: string, + validUntil: number, + providerUri: string, + signer: Signer, + signal?: AbortSignal + ): Promise { + const providerEndpoints = await this.getEndpoints(providerUri) + const serviceEndpoints = await this.getServiceEndpoints( + providerUri, + providerEndpoints + ) + + // Diff from V1. We might need a signature to get the files object, specially if dealing with confidential evm and template 4 + // otherwise it can be ignored + const consumerAddress = await signer.getAddress() + const nonce = ( + (await this.getNonce( + providerUri, + consumerAddress, + signal, + providerEndpoints, + serviceEndpoints + )) + 1 + ).toString() + + // same signed message as for start compute (consumer address + did[0] + nonce) + let signatureMessage = consumerAddress + signatureMessage += assets[0].documentId + signatureMessage += nonce + const signature = await this.signProviderRequest(signer, signatureMessage) + + const providerData = { + datasets: assets, + algorithm, + compute: { env: computeEnv, validUntil }, + consumerAddress, + signature + } + const initializeUrl = this.getEndpointURL(serviceEndpoints, 'initializeCompute') + ? this.getEndpointURL(serviceEndpoints, 'initializeCompute').urlPath + : null + if (!initializeUrl) return null + + let response + try { + response = await fetch(initializeUrl, { + method: 'POST', + body: JSON.stringify(providerData), + headers: { 'Content-Type': 'application/json' }, + signal + }) + } catch (e) { + LoggerInstance.error('Initialize compute failed: ') + LoggerInstance.error(e) + throw new Error('ComputeJob cannot be initialized') + } + if (response?.ok) { + const params = await response.json() + return params + } + const resolvedResponse = await response.json() + LoggerInstance.error( + 'Initialize compute failed: ', + response.status, + response.statusText, + resolvedResponse + ) + LoggerInstance.error('Payload was:', providerData) + throw new Error(JSON.stringify(resolvedResponse)) + } + /** * Gets the download URL. * @param {string} did - The DID. @@ -530,7 +616,7 @@ export class Provider { return consumeUrl } - /** Instruct the provider to start a compute job + /** Instruct the provider to start a compute job (Old C2D V1) Kept for now, for backwards compatibility * @param {string} providerUri The provider URI. * @param {Signer} signer The consumer signer object. * @param {string} computeEnv The compute environment. @@ -540,8 +626,9 @@ export class Provider { * @param {ComputeAsset[]} additionalDatasets The additional datasets if that is the case. * @param {ComputeOutput} output The compute job output settings. * @return {Promise} The compute job or jobs. + * @deprecated Use {@link computeStart} instead. */ - public async computeStart( + public async computeStartV1( providerUri: string, consumer: Signer, computeEnv: string, @@ -613,6 +700,305 @@ export class Provider { return null } + /** Instruct the provider to start a PAYED compute job (new C2D V2) + * @param {string} providerUri The provider URI. + * @param {Signer} signer The consumer signer object. + * @param {string} computeEnv The compute environment. + * @param {ComputeAsset} datasets The dataset to start compute on + additionalDatasets (the additional datasets if that is the case) + * @param {ComputeAlgorithm} algorithm The algorithm to start compute with. + * @param {ComputeResourceRequest} resources The resources to start compute job with. + * @param {chainId} chainId The chain used to do payments + * @param {ComputeOutput} output The compute job output settings. + * @param {boolean} freeEnvironment is it a free environment? uses different route + * @param {AbortSignal} signal abort signal + * @return {Promise} The compute job or jobs. + */ + public async computeStart( + providerUri: string, + consumer: Signer, + computeEnv: string, + datasets: ComputeAsset[], + algorithm: ComputeAlgorithm, + resources?: ComputeResourceRequest[], + chainId?: number, // network used by payment (only for payed compute jobs) + output?: ComputeOutput, + signal?: AbortSignal + ): Promise { + console.log('called new compute start method...') + console.log('datasets: ', datasets) + console.log('algorithm: ', algorithm) + const providerEndpoints = await this.getEndpoints(providerUri) + const serviceEndpoints = await this.getServiceEndpoints( + providerUri, + providerEndpoints + ) + + const computeStartUrl = this.getEndpointURL(serviceEndpoints, 'computeStart') + ? this.getEndpointURL(serviceEndpoints, 'computeStart').urlPath + : null + + if (!computeStartUrl) { + LoggerInstance.error( + 'Compute start failed: Cannot get proper computeStart route (perhaps not implemented on provider?)' + ) + return null + } + + const consumerAddress = await consumer.getAddress() + const nonce = ( + (await this.getNonce( + providerUri, + consumerAddress, + signal, + providerEndpoints, + serviceEndpoints + )) + 1 + ).toString() + + let signatureMessage = consumerAddress + signatureMessage += datasets[0].documentId + signatureMessage += nonce + const signature = await this.signProviderRequest(consumer, signatureMessage) + const payload = Object() + payload.consumerAddress = consumerAddress + payload.signature = signature + payload.nonce = nonce + payload.environment = computeEnv + payload.resources = resources + payload.chainId = chainId + // kept for backwards compatibility (tests running against existing provider) + payload.dataset = datasets[0] + // new field for C2D v2 + payload.datasets = datasets + payload.algorithm = algorithm + // if (additionalDatasets) payload.additionalDatasets = additionalDatasets + payload.output = output + let response + try { + response = await fetch(computeStartUrl, { + method: 'POST', + body: JSON.stringify(payload), + headers: { 'Content-Type': 'application/json' }, + signal + }) + } catch (e) { + LoggerInstance.error('Compute start failed:') + LoggerInstance.error(e) + LoggerInstance.error('Payload was:', payload) + throw new Error('HTTP request failed calling Provider') + } + if (response?.ok) { + const params = await response.json() + return params + } + LoggerInstance.error( + 'Compute start failed: ', + response.status, + response.statusText, + await response.json() + ) + LoggerInstance.error('Payload was:', payload) + return null + } + + /** Instruct the provider to start a FREE compute job (new C2D V2) + * @param {string} providerUri The provider URI. + * @param {Signer} signer The consumer signer object. + * @param {string} computeEnv The compute environment. + * @param {ComputeAsset} datasets The dataset to start compute on + additionalDatasets (the additional datasets if that is the case) + * @param {ComputeAlgorithm} algorithm The algorithm to start compute with. + * @param {ComputeResourceRequest} resources The resources to start compute job with. + * @param {ComputeOutput} output The compute job output settings. + * @param {AbortSignal} signal abort signal + * @return {Promise} The compute job or jobs. + */ + public async freeComputeStart( + providerUri: string, + consumer: Signer, + computeEnv: string, + datasets: ComputeAsset[], + algorithm: ComputeAlgorithm, + resources?: ComputeResourceRequest[], + output?: ComputeOutput, + signal?: AbortSignal + ): Promise { + console.log('called new free compute start method...') + console.log('datasets: ', datasets) + console.log('algorithm: ', algorithm) + const providerEndpoints = await this.getEndpoints(providerUri) + const serviceEndpoints = await this.getServiceEndpoints( + providerUri, + providerEndpoints + ) + + const computeStartUrl = this.getEndpointURL(serviceEndpoints, 'freeCompute') + ? this.getEndpointURL(serviceEndpoints, 'freeCompute').urlPath + : null + + if (!computeStartUrl) { + LoggerInstance.error( + 'Compute start failed: Cannot get proper computeStart route (perhaps not implemented on provider?)' + ) + return null + } + + const consumerAddress = await consumer.getAddress() + const nonce = ( + (await this.getNonce( + providerUri, + consumerAddress, + signal, + providerEndpoints, + serviceEndpoints + )) + 1 + ).toString() + + const signatureMessage = nonce // datasets[0].documentId + console.log('signatureMessage: ', signatureMessage) + const signature = await this.signProviderRequest(consumer, signatureMessage) + const payload = Object() + payload.consumerAddress = consumerAddress + payload.signature = signature + payload.nonce = nonce + payload.environment = computeEnv + payload.resources = resources + // kept for backwards compatibility (tests running against existing provider) + payload.dataset = datasets[0] + // new field for C2D v2 + payload.datasets = datasets + payload.algorithm = algorithm + // if (additionalDatasets) payload.additionalDatasets = additionalDatasets + payload.output = output + let response + try { + response = await fetch(computeStartUrl, { + method: 'POST', + body: JSON.stringify(payload), + headers: { 'Content-Type': 'application/json' }, + signal + }) + } catch (e) { + LoggerInstance.error('Compute start failed:') + LoggerInstance.error(e) + LoggerInstance.error('Payload was:', payload) + throw new Error('HTTP request failed calling Provider') + } + if (response?.ok) { + const params = await response.json() + return params + } + LoggerInstance.error( + 'Compute start failed: ', + response.status, + response.statusText, + await response.json() + ) + LoggerInstance.error('Payload was:', payload) + return null + } + + /** + * + * @param providerUri provider URL + * @param consumer consumer + * @param jobId jobId + * @param signal abort signal + * @returns logs response + */ + public async computeStreamableLogs( + providerUri: string, + signer: Signer, + jobId: string, + signal?: AbortSignal + ): Promise { + const providerEndpoints = await this.getEndpoints(providerUri) + const serviceEndpoints = await this.getServiceEndpoints( + providerUri, + providerEndpoints + ) + + const computeStreamableLogs = this.getEndpointURL( + serviceEndpoints, + 'computeStreamableLogs' + ) + ? this.getEndpointURL(serviceEndpoints, 'computeStreamableLogs').urlPath + : null + + if (!computeStreamableLogs) { + LoggerInstance.error( + 'Compute start failed: Cannot get proper computeStreamableLogs route (perhaps not implemented on provider?)' + ) + return null + } + const consumerAddress = await signer.getAddress() + const nonce = ( + (await this.getNonce( + providerUri, + consumerAddress, + signal, + providerEndpoints, + serviceEndpoints + )) + 1 + ).toString() + + let url = `?consumerAddress=${consumerAddress}` + url += `&jobId=${jobId}` + url += `&nonce=${nonce}` + + // TODO: define teh signature to use (not implemented yet on node) + const signatureMessage = nonce + const signature = await this.signProviderRequest(signer, signatureMessage) + url += `&signature=${signature}` + + let response + try { + response = await fetch(computeStreamableLogs + url, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + signal + }) + console.log('Raw response:', response) + } catch (e) { + LoggerInstance.error('computeStreamableLogs failed:') + LoggerInstance.error(e) + throw new Error('HTTP request failed calling Provider') + } + if (response?.ok || response?.status === 200) { + // do not handle the response here + console.log('Response body:', response.body) + return response.body + } + LoggerInstance.error( + 'computeStreamableLogs failed: ', + response.status, + response.statusText, + await response.json() + ) + return null + } + + public async getComputeStartRoutes( + providerUri: string, + isFreeCompute: boolean = false + ): Promise { + const providerEndpoints = await this.getEndpoints(providerUri) + const serviceEndpoints = await this.getServiceEndpoints( + providerUri, + providerEndpoints + ) + let computeStartUrl = null + if (isFreeCompute) { + computeStartUrl = this.getEndpointURL(serviceEndpoints, 'freeCompute') + ? this.getEndpointURL(serviceEndpoints, 'freeCompute').urlPath + : null + } else { + computeStartUrl = this.getEndpointURL(serviceEndpoints, 'computeStart') + ? this.getEndpointURL(serviceEndpoints, 'computeStart').urlPath + : null + } + return computeStartUrl + } + /** Instruct the provider to Stop the execution of a to stop a compute job. * @param {string} did the asset did * @param {string} consumerAddress The consumer address. @@ -661,7 +1047,7 @@ export class Provider { const signature = await this.signProviderRequest(signer, signatureMessage) const payload = Object() payload.signature = signature - payload.agreementId = this.noZeroX(agreementId) + payload.agreementId = agreementId // this.noZeroX(agreementId) #https://github.com/oceanprotocol/ocean.js/issues/1892 payload.consumerAddress = consumerAddress payload.nonce = nonce if (jobId) payload.jobId = jobId @@ -722,7 +1108,7 @@ export class Provider { : null let url = `?consumerAddress=${consumerAddress}` - url += (agreementId && `&agreementId=${this.noZeroX(agreementId)}`) || '' + url += (agreementId && `&agreementId=${agreementId}`) || '' // ${this.noZeroX(agreementId)} #https://github.com/oceanprotocol/ocean.js/issues/1892 url += (jobId && `&jobId=${jobId}`) || '' if (!computeStatusUrl) return null @@ -847,7 +1233,7 @@ export class Provider { signatureMessage += nonce const signature = await this.signProviderRequest(consumer, signatureMessage) const payload = Object() - payload.documentId = this.noZeroX(did) + payload.documentId = did // this.noZeroX(did) #https://github.com/oceanprotocol/ocean.js/issues/1892 payload.consumerAddress = await consumer.getAddress() payload.jobId = jobId if (signature) payload.signature = signature diff --git a/src/utils/Assets.ts b/src/utils/Assets.ts index f9a34421b..65c377c12 100644 --- a/src/utils/Assets.ts +++ b/src/utils/Assets.ts @@ -17,7 +17,7 @@ import AccessListFactory from '@oceanprotocol/contracts/artifacts/contracts/acce import ERC20Template4 from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC20Template4.sol/ERC20Template4.json' import { calculateActiveTemplateIndex } from './Addresses.js' import { DDOManager } from '@oceanprotocol/ddo-js' -// import * as hre from 'hardhat' +import { FileObjectType } from '../@types' export const DEVELOPMENT_CHAIN_ID = 8996 // template address OR templateId @@ -114,6 +114,15 @@ export async function createAsset( mpFeeAddress: ZERO_ADDRESS } + if ( + !assetUrl.type || + ![FileObjectType.ARWEAVE, FileObjectType.IPFS, FileObjectType.URL].includes( + assetUrl.type.toLowerCase() + ) + ) { + console.log('Missing or invalid files object type, defaulting to "url"') + assetUrl.type = FileObjectType.URL + } // include fileObject in the DT constructor if (config.sdk === 'oasis') { datatokenParams.filesObject = assetUrl diff --git a/src/utils/General.ts b/src/utils/General.ts index 71e441a25..4e5cfc00a 100644 --- a/src/utils/General.ts +++ b/src/utils/General.ts @@ -7,3 +7,7 @@ export async function sleep(ms: number) { setTimeout(resolve, ms) }) } + +export function isDefined(something: any): boolean { + return something !== undefined && something !== null +} diff --git a/test/integration/ComputeExamples.test.ts b/test/integration/ComputeExamples.test.ts index 8f89c9141..a1bf662a4 100644 --- a/test/integration/ComputeExamples.test.ts +++ b/test/integration/ComputeExamples.test.ts @@ -143,7 +143,8 @@ import { configHelperNetworks, ConfigHelper, getEventFromTx, - amountToUnits + amountToUnits, + isDefined } from '../../src/index.js' /// ``` import crypto from 'crypto-js' @@ -276,6 +277,9 @@ let resolvedAlgorithmDdo: DDO let computeJobId: string let agreementId: string + +let computeRoutePath: string +let hasFreeComputeSupport: boolean /// ``` /// ### 4.3 Helper methods @@ -595,7 +599,7 @@ describe('Compute-to-data example tests', async () => { /// let's check the free compute environment /// ```Typescript const computeEnv = computeEnvs[resolvedDatasetDdo.chainId].find( - (ce) => ce.priceMin === 0 + (ce) => ce.priceMin === 0 || isDefined(ce.free) ) console.log('Free compute environment = ', computeEnv) /// ``` @@ -603,111 +607,135 @@ describe('Compute-to-data example tests', async () => { assert(computeEnv, 'Cannot find the free compute env') /// --> - /// Let's have 5 minute of compute access - /// ```Typescript - const mytime = new Date() - const computeMinutes = 5 - mytime.setMinutes(mytime.getMinutes() + computeMinutes) - const computeValidUntil = Math.floor(mytime.getTime() / 1000) - - const assets: ComputeAsset[] = [ - { - documentId: resolvedDatasetDdo.id, - serviceId: resolvedDatasetDdo.services[0].id + computeRoutePath = await ProviderInstance.getComputeStartRoutes(providerUrl, true) + if (isDefined(computeRoutePath)) { + hasFreeComputeSupport = true + /// Let's have 5 minute of compute access + /// ```Typescript + const mytime = new Date() + const computeMinutes = 5 + mytime.setMinutes(mytime.getMinutes() + computeMinutes) + const computeValidUntil = Math.floor(mytime.getTime() / 1000) + + const assets: ComputeAsset[] = [ + { + documentId: resolvedDatasetDdo.id, + serviceId: resolvedDatasetDdo.services[0].id + } + ] + const dtAddressArray = [resolvedDatasetDdo.services[0].datatokenAddress] + const algo: ComputeAlgorithm = { + documentId: resolvedAlgorithmDdo.id, + serviceId: resolvedAlgorithmDdo.services[0].id } - ] - const dtAddressArray = [resolvedDatasetDdo.services[0].datatokenAddress] - const algo: ComputeAlgorithm = { - documentId: resolvedAlgorithmDdo.id, - serviceId: resolvedAlgorithmDdo.services[0].id - } - const providerInitializeComputeResults = await ProviderInstance.initializeCompute( - assets, - algo, - computeEnv.id, - computeValidUntil, - providerUrl, - await consumerAccount.getAddress() - ) - /// ``` - /// - /// ```Typescript - algo.transferTxId = await handleOrder( - providerInitializeComputeResults.algorithm, - resolvedAlgorithmDdo.services[0].datatokenAddress, - consumerAccount, - computeEnv.consumerAddress, - 0 - ) - for (let i = 0; i < providerInitializeComputeResults.datasets.length; i++) { - assets[i].transferTxId = await handleOrder( - providerInitializeComputeResults.datasets[i], - dtAddressArray[i], + const providerInitializeComputeResults = await ProviderInstance.initializeCompute( + assets, + algo, + computeEnv.id, + computeValidUntil, + providerUrl, + consumerAccount + ) + /// ``` + /// + /// ```Typescript + algo.transferTxId = await handleOrder( + providerInitializeComputeResults.algorithm, + resolvedAlgorithmDdo.services[0].datatokenAddress, consumerAccount, computeEnv.consumerAddress, 0 ) - } + for (let i = 0; i < providerInitializeComputeResults.datasets.length; i++) { + assets[i].transferTxId = await handleOrder( + providerInitializeComputeResults.datasets[i], + dtAddressArray[i], + consumerAccount, + computeEnv.consumerAddress, + 0 + ) + } - const computeJobs = await ProviderInstance.computeStart( - providerUrl, - consumerAccount, - computeEnv.id, - assets[0], - algo - ) + const computeJobs = await ProviderInstance.freeComputeStart( + providerUrl, + consumerAccount, + computeEnv.id, + assets, + algo + ) - /// ``` - /// - /// Let's save the compute job it, we re going to use later - /// ```Typescript - computeJobId = computeJobs[0].jobId - // eslint-disable-next-line prefer-destructuring - agreementId = computeJobs[0].agreementId + /// ``` + /// + /// Let's save the compute job it, we re going to use later + /// ```Typescript + computeJobId = computeJobs[0].jobId + // eslint-disable-next-line prefer-destructuring + agreementId = computeJobs[0].agreementId + } else { + assert( + computeRoutePath === null, + 'Route path for free compute is not defined (perhaps because provider does not support it yet?)' + ) + hasFreeComputeSupport = false + } }) /// /// ``` /// ## 11. Check compute status and get download compute results URL it('11.1 Check compute status', async () => { - /// You can also add various delays so you see the various states of the compute job - /// ```Typescript - const jobStatus = await ProviderInstance.computeStatus( - providerUrl, - await consumerAccount.getAddress(), - computeJobId, - agreementId - ) - /// ``` - /// - /// Now, let's see the current status of the previously started computer job - /// ```Typescript - console.log('Current status of the compute job: ', jobStatus) + if (!hasFreeComputeSupport) { + assert( + computeRoutePath === null, + 'Compute route path for free compute is not defined (perhaps because provider does not support it yet?)' + ) + } else { + /// You can also add various delays so you see the various states of the compute job + /// ```Typescript + const jobStatus = await ProviderInstance.computeStatus( + providerUrl, + await consumerAccount.getAddress(), + computeJobId, + agreementId + ) + /// ``` + /// + /// Now, let's see the current status of the previously started computer job + /// ```Typescript + console.log('Current status of the compute job: ', jobStatus) + } }) /// /// ``` it('11.2 Get download compute results URL', async () => { - /// ```Typescript - await sleep(10000) - const downloadURL = await ProviderInstance.getComputeResultUrl( - providerUrl, - consumerAccount, - computeJobId, - 0 - ) - /// ``` - /// - /// Let's check the compute results url for the specified index - /// ```Typescript - console.log(`Compute results URL: ${downloadURL}`) + if (!hasFreeComputeSupport) { + assert( + computeRoutePath === null, + 'Compute route path for free compute is not defined (perhaps because provider does not support it yet?)' + ) + } else { + /// ```Typescript + await sleep(10000) + const downloadURL = await ProviderInstance.getComputeResultUrl( + providerUrl, + consumerAccount, + computeJobId, + 0 + ) + /// ``` + /// + /// Let's check the compute results url for the specified index + /// ```Typescript + console.log(`Compute results URL: ${downloadURL}`) + } }) /// /// ``` }) /// diff --git a/test/integration/ComputeFlow.test.ts b/test/integration/ComputeFlow.test.ts index 8e8fbc5a5..a15e68bb0 100644 --- a/test/integration/ComputeFlow.test.ts +++ b/test/integration/ComputeFlow.test.ts @@ -7,7 +7,8 @@ import { Aquarius, Datatoken, sendTx, - amountToUnits + amountToUnits, + isDefined } from '../../src/index.js' import { ComputeJob, @@ -46,6 +47,8 @@ let paidEnvDatasetTxId let paidEnvAlgoTxId let computeValidUntil +let freeComputeRouteSupport = null + const assetUrl: Files = { datatokenAddress: '0x0', nftAddress: '0x0', @@ -413,7 +416,7 @@ describe('Compute flow tests', async () => { // we choose the free env const computeEnv = computeEnvs[resolvedDdoWith5mTimeout.chainId].find( - (ce) => ce.priceMin === 0 + (ce) => ce.priceMin === 0 || isDefined(ce.free) ) assert(computeEnv, 'Cannot find the free compute env') @@ -435,7 +438,7 @@ describe('Compute flow tests', async () => { computeEnv.id, computeValidUntil, providerUrl, - await consumerAccount.getAddress() + consumerAccount ) assert( !('error' in providerInitializeComputeResults.algorithm), @@ -462,83 +465,106 @@ describe('Compute flow tests', async () => { config ) } - const computeJobs = await ProviderInstance.computeStart( + + freeComputeRouteSupport = await ProviderInstance.getComputeStartRoutes( providerUrl, - consumerAccount, - computeEnv.id, - assets[0], - algo + true ) - freeEnvDatasetTxId = assets[0].transferTxId - freeEnvAlgoTxId = algo.transferTxId - assert(computeJobs, 'Cannot start compute job') - freeComputeJobId = computeJobs[0].jobId + if (freeComputeRouteSupport) { + const computeJobs = await ProviderInstance.freeComputeStart( + providerUrl, + consumerAccount, + computeEnv.id, + assets, + algo + ) + freeEnvDatasetTxId = assets[0].transferTxId + freeEnvAlgoTxId = algo.transferTxId + assert(computeJobs, 'Cannot start compute job') + freeComputeJobId = computeJobs[0].jobId + + delay(100000) + + const jobFinished = await waitTillJobEnds() + console.log('Job finished: ', jobFinished) + } else { + assert( + freeComputeRouteSupport === null, + 'Cannot start free compute job. provider at ' + + providerUrl + + ' does not implement freeCompute route' + ) + } }) - delay(100000) - - const jobFinished = await waitTillJobEnds() - console.log('Job finished: ', jobFinished) - // move to start orders with initial txid's and provider fees it('should restart a computeJob without paying anything, because order is valid and providerFees are still valid', async () => { // we choose the free env - const computeEnv = computeEnvs[resolvedDdoWith5mTimeout.chainId].find( - (ce) => ce.priceMin === 0 - ) - assert(computeEnv, 'Cannot find the free compute env') - - const assets: ComputeAsset[] = [ - { - documentId: resolvedDdoWith5mTimeout.id, - serviceId: resolvedDdoWith5mTimeout.services[0].id, - transferTxId: freeEnvDatasetTxId + if (freeComputeRouteSupport) { + const computeEnv = computeEnvs[resolvedDdoWith5mTimeout.chainId].find( + (ce) => ce.priceMin === 0 || isDefined(ce.free) + ) + assert(computeEnv, 'Cannot find the free compute env') + + const assets: ComputeAsset[] = [ + { + documentId: resolvedDdoWith5mTimeout.id, + serviceId: resolvedDdoWith5mTimeout.services[0].id, + transferTxId: freeEnvDatasetTxId + } + ] + const algo: ComputeAlgorithm = { + documentId: resolvedAlgoDdoWith5mTimeout.id, + serviceId: resolvedAlgoDdoWith5mTimeout.services[0].id, + transferTxId: freeEnvAlgoTxId } - ] - const algo: ComputeAlgorithm = { - documentId: resolvedAlgoDdoWith5mTimeout.id, - serviceId: resolvedAlgoDdoWith5mTimeout.services[0].id, - transferTxId: freeEnvAlgoTxId + providerInitializeComputeResults = await ProviderInstance.initializeCompute( + assets, + algo, + computeEnv.id, + computeValidUntil, + providerUrl, + consumerAccount + ) + assert( + providerInitializeComputeResults.algorithm.validOrder, + 'We should have a valid order for algorithm' + ) + assert( + !providerInitializeComputeResults.algorithm.providerFee, + 'We should not pay providerFees again for algorithm' + ) + assert( + providerInitializeComputeResults.datasets[0].validOrder, + 'We should have a valid order for dataset' + ) + assert( + !providerInitializeComputeResults.datasets[0].providerFee, + 'We should not pay providerFees again for dataset' + ) + algo.transferTxId = providerInitializeComputeResults.algorithm.validOrder + assets[0].transferTxId = providerInitializeComputeResults.datasets[0].validOrder + assert( + algo.transferTxId === freeEnvAlgoTxId && + assets[0].transferTxId === freeEnvDatasetTxId, + 'We should use the same orders, because no fess must be paid' + ) + const computeJobs = await ProviderInstance.computeStart( + providerUrl, + consumerAccount, + computeEnv.id, + assets, + algo + ) + assert(computeJobs, 'Cannot start compute job') + } else { + assert( + freeComputeRouteSupport === null, + 'Cannot start free compute job. provider at ' + + providerUrl + + ' does not implement freeCompute route' + ) } - providerInitializeComputeResults = await ProviderInstance.initializeCompute( - assets, - algo, - computeEnv.id, - computeValidUntil, - providerUrl, - await consumerAccount.getAddress() - ) - assert( - providerInitializeComputeResults.algorithm.validOrder, - 'We should have a valid order for algorithm' - ) - assert( - !providerInitializeComputeResults.algorithm.providerFee, - 'We should not pay providerFees again for algorithm' - ) - assert( - providerInitializeComputeResults.datasets[0].validOrder, - 'We should have a valid order for dataset' - ) - assert( - !providerInitializeComputeResults.datasets[0].providerFee, - 'We should not pay providerFees again for dataset' - ) - algo.transferTxId = providerInitializeComputeResults.algorithm.validOrder - assets[0].transferTxId = providerInitializeComputeResults.datasets[0].validOrder - assert( - algo.transferTxId === freeEnvAlgoTxId && - assets[0].transferTxId === freeEnvDatasetTxId, - 'We should use the same orders, because no fess must be paid' - ) - const computeJobs = await ProviderInstance.computeStart( - providerUrl, - consumerAccount, - computeEnv.id, - assets[0], - algo - ) - assert(computeJobs, 'Cannot start compute job') }) // // moving to paid environments @@ -546,7 +572,7 @@ describe('Compute flow tests', async () => { it('should start a computeJob on a paid environment', async () => { // we choose the paid env const computeEnv = computeEnvs[resolvedDdoWith5mTimeout.chainId].find( - (ce) => ce.priceMin !== 0 + (ce) => ce.priceMin !== 0 || !isDefined(ce.free) ) assert(computeEnv, 'Cannot find the paid compute env') @@ -568,7 +594,7 @@ describe('Compute flow tests', async () => { computeEnv.id, computeValidUntil, providerUrl, - await consumerAccount.getAddress() + consumerAccount ) assert( !('error' in providerInitializeComputeResults.algorithm), @@ -599,7 +625,7 @@ describe('Compute flow tests', async () => { providerUrl, consumerAccount, computeEnv.id, - assets[0], + assets, algo ) paidEnvDatasetTxId = assets[0].transferTxId @@ -623,9 +649,9 @@ describe('Compute flow tests', async () => { it('should restart a computeJob on paid environment, without paying anything, because order is valid and providerFees are still valid', async () => { // we choose the paid env const computeEnv = computeEnvs[resolvedDdoWith5mTimeout.chainId].find( - (ce) => ce.priceMin !== 0 + (ce) => ce.priceMin !== 0 || !isDefined(ce.free) ) - assert(computeEnv, 'Cannot find the free compute env') + assert(computeEnv, 'Cannot find the paid compute env') const assets: ComputeAsset[] = [ { @@ -646,7 +672,7 @@ describe('Compute flow tests', async () => { computeEnv.id, computeValidUntil, providerUrl, - await consumerAccount.getAddress() + consumerAccount ) assert( providerInitializeComputeResults.algorithm.validOrder, @@ -675,7 +701,7 @@ describe('Compute flow tests', async () => { providerUrl, consumerAccount, computeEnv.id, - assets[0], + assets, algo ) assert(computeJobs, 'Cannot start compute job') @@ -691,96 +717,105 @@ describe('Compute flow tests', async () => { }) it('should start a computeJob using the free environment, by paying only providerFee (reuseOrder)', async () => { - // we choose the free env - const computeEnv = computeEnvs[resolvedDdoWith5mTimeout.chainId].find( - (ce) => ce.priceMin === 0 - ) - assert(computeEnv, 'Cannot find the free compute env') - - const assets: ComputeAsset[] = [ - { - documentId: resolvedDdoWith5mTimeout.id, - serviceId: resolvedDdoWith5mTimeout.services[0].id, - transferTxId: freeEnvDatasetTxId + if (freeComputeRouteSupport) { + // we choose the free env + const computeEnv = computeEnvs[resolvedDdoWith5mTimeout.chainId].find( + (ce) => ce.priceMin === 0 || isDefined(ce.free) + ) + assert(computeEnv, 'Cannot find the free compute env') + + const assets: ComputeAsset[] = [ + { + documentId: resolvedDdoWith5mTimeout.id, + serviceId: resolvedDdoWith5mTimeout.services[0].id, + transferTxId: freeEnvDatasetTxId + } + ] + const dtAddressArray = [resolvedDdoWith5mTimeout.services[0].datatokenAddress] + const algo: ComputeAlgorithm = { + documentId: resolvedAlgoDdoWith5mTimeout.id, + serviceId: resolvedAlgoDdoWith5mTimeout.services[0].id, + transferTxId: freeEnvAlgoTxId } - ] - const dtAddressArray = [resolvedDdoWith5mTimeout.services[0].datatokenAddress] - const algo: ComputeAlgorithm = { - documentId: resolvedAlgoDdoWith5mTimeout.id, - serviceId: resolvedAlgoDdoWith5mTimeout.services[0].id, - transferTxId: freeEnvAlgoTxId - } - providerInitializeComputeResults = await ProviderInstance.initializeCompute( - assets, - algo, - computeEnv.id, - computeValidUntil, - providerUrl, - await consumerAccount.getAddress() - ) - assert( - providerInitializeComputeResults.algorithm.validOrder, - 'We should have a valid order for algorithm' - ) - assert( - providerInitializeComputeResults.datasets[0].validOrder, - 'We should have a valid order for dataset' - ) + providerInitializeComputeResults = await ProviderInstance.initializeCompute( + assets, + algo, + computeEnv.id, + computeValidUntil, + providerUrl, + consumerAccount + ) + assert( + providerInitializeComputeResults.algorithm.validOrder, + 'We should have a valid order for algorithm' + ) + assert( + providerInitializeComputeResults.datasets[0].validOrder, + 'We should have a valid order for dataset' + ) - assert( - providerInitializeComputeResults.algorithm.providerFee || - providerInitializeComputeResults.datasets[0].providerFee, - 'We should pay providerFees again for algorithm or dataset. Cannot have empty for both' - ) + assert( + providerInitializeComputeResults.algorithm.providerFee || + providerInitializeComputeResults.datasets[0].providerFee, + 'We should pay providerFees again for algorithm or dataset. Cannot have empty for both' + ) - assert( - !('error' in providerInitializeComputeResults.algorithm), - 'Cannot order algorithm' - ) - algo.transferTxId = await handleComputeOrder( - providerInitializeComputeResults.algorithm, - resolvedAlgoDdoWith5mTimeout.services[0].datatokenAddress, - consumerAccount, - computeEnv.consumerAddress, - 0, - datatoken, - config - ) - for (let i = 0; i < providerInitializeComputeResults.datasets.length; i++) { - assets[i].transferTxId = await handleComputeOrder( - providerInitializeComputeResults.datasets[i], - dtAddressArray[i], + assert( + !('error' in providerInitializeComputeResults.algorithm), + 'Cannot order algorithm' + ) + algo.transferTxId = await handleComputeOrder( + providerInitializeComputeResults.algorithm, + resolvedAlgoDdoWith5mTimeout.services[0].datatokenAddress, consumerAccount, computeEnv.consumerAddress, 0, datatoken, config ) + for (let i = 0; i < providerInitializeComputeResults.datasets.length; i++) { + assets[i].transferTxId = await handleComputeOrder( + providerInitializeComputeResults.datasets[i], + dtAddressArray[i], + consumerAccount, + computeEnv.consumerAddress, + 0, + datatoken, + config + ) + } + assert( + algo.transferTxId !== freeEnvAlgoTxId || + assets[0].transferTxId !== freeEnvDatasetTxId, + 'We should not use the same orders, because providerFee must be paid' + ) + const computeJobs = await ProviderInstance.computeStart( + providerUrl, + consumerAccount, + computeEnv.id, + assets, + algo + ) + // freeEnvDatasetTxId = assets[0].transferTxId + // freeEnvAlgoTxId = algo.transferTxId + assert(computeJobs, 'Cannot start compute job') + } else { + assert( + freeComputeRouteSupport === null, + 'Cannot start free compute job. provider at ' + + providerUrl + + ' does not implement freeCompute route' + ) } - assert( - algo.transferTxId !== freeEnvAlgoTxId || - assets[0].transferTxId !== freeEnvDatasetTxId, - 'We should not use the same orders, because providerFee must be paid' - ) - const computeJobs = await ProviderInstance.computeStart( - providerUrl, - consumerAccount, - computeEnv.id, - assets[0], - algo - ) - // freeEnvDatasetTxId = assets[0].transferTxId - // freeEnvAlgoTxId = algo.transferTxId - assert(computeJobs, 'Cannot start compute job') }) it('should start a computeJob using the paid environment, by paying only providerFee (reuseOrder)', async () => { // we choose the paid env const computeEnv = computeEnvs[resolvedDdoWith5mTimeout.chainId].find( - (ce) => ce.priceMin !== 0 + (ce) => ce.priceMin !== 0 || !isDefined(ce.free) ) - assert(computeEnv, 'Cannot find the free compute env') + assert(computeEnv, 'Cannot find the paid compute env') const assets: ComputeAsset[] = [ { @@ -802,7 +837,7 @@ describe('Compute flow tests', async () => { computeEnv.id, computeValidUntil, providerUrl, - await consumerAccount.getAddress() + consumerAccount ) assert( providerInitializeComputeResults.algorithm.validOrder, @@ -851,7 +886,7 @@ describe('Compute flow tests', async () => { providerUrl, consumerAccount, computeEnv.id, - assets[0], + assets, algo ) // freeEnvDatasetTxId = assets[0].transferTxId diff --git a/test/integration/helpers.ts b/test/integration/helpers.ts index 91caccfcd..8d1f1bcff 100644 --- a/test/integration/helpers.ts +++ b/test/integration/helpers.ts @@ -138,7 +138,8 @@ export async function handleComputeOrder( if (config.chainId !== chainID) { throw new Error('Chain ID from DDO is different than the configured network.') } - if (order.providerFee && order.providerFee.providerFeeAmount) { + const hasProviderFees = order.providerFee && order.providerFee.providerFeeAmount + if (hasProviderFees && Number(order.providerFee.providerFeeAmount) > 0) { await approveWei( payerAccount, config,