mirror of
https://github.com/oceanprotocol/ocean.js.git
synced 2024-11-26 20:39:05 +01:00
Custom user parameters (#944)
* enable create / passthrough of userData & algoData * refactor bassed on reviews * refactor balance check async flows * more provider logs * more debugs for provider * provider debug * more provider logs * more provider debug * revert ci changes * naming & typings * Release 0.18.0-next.0 * deal with pre-releases * clarify instructions * fix merge errors Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
ae1c2ff22f
commit
02cc911ce9
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -111,6 +111,7 @@ jobs:
|
|||||||
cd .. && ./scripts/waitforcontracts.sh
|
cd .. && ./scripts/waitforcontracts.sh
|
||||||
|
|
||||||
- run: npm run test:integration:cover
|
- run: npm run test:integration:cover
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: coverage
|
name: coverage
|
||||||
|
11
.github/workflows/publish.yml
vendored
11
.github/workflows/publish.yml
vendored
@ -8,6 +8,8 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
npm:
|
npm:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
@ -15,6 +17,11 @@ jobs:
|
|||||||
node-version: '16'
|
node-version: '16'
|
||||||
registry-url: https://registry.npmjs.org/
|
registry-url: https://registry.npmjs.org/
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
|
||||||
|
# pre-releases, triggered by `next` as part of git tag
|
||||||
|
- run: npm publish --tag next
|
||||||
|
if: ${{ contains(github.ref, 'next') }}
|
||||||
|
|
||||||
|
# production releases
|
||||||
- run: npm publish
|
- run: npm publish
|
||||||
env:
|
if: ${{ !contains(github.ref, 'next') }}
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
|
11
README.md
11
README.md
@ -42,6 +42,7 @@ This is in alpha state and you can expect running into problems. If you run into
|
|||||||
- [🛳 Production](#-production)
|
- [🛳 Production](#-production)
|
||||||
- [⬆️ Releases](#️-releases)
|
- [⬆️ Releases](#️-releases)
|
||||||
- [Production](#production)
|
- [Production](#production)
|
||||||
|
- [Pre-releases](#pre-releases)
|
||||||
- [🏛 License](#-license)
|
- [🏛 License](#-license)
|
||||||
|
|
||||||
## 📚 Prerequisites
|
## 📚 Prerequisites
|
||||||
@ -229,6 +230,16 @@ The task does the following:
|
|||||||
|
|
||||||
For the GitHub releases steps a GitHub personal access token, exported as `GITHUB_TOKEN` is required. [Setup](https://github.com/release-it/release-it#github-releases)
|
For the GitHub releases steps a GitHub personal access token, exported as `GITHUB_TOKEN` is required. [Setup](https://github.com/release-it/release-it#github-releases)
|
||||||
|
|
||||||
|
### Pre-Releases
|
||||||
|
|
||||||
|
For pre-releases, this is required for the first one like `v0.18.0-next.0`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./node_modules/.bin/release-it major|minor|patch --preRelease=next
|
||||||
|
```
|
||||||
|
|
||||||
|
Further releases afterwards can be done with `npm run release` again and selecting the appropriate next version, in this case `v0.18.0-next.1` and so on.
|
||||||
|
|
||||||
## 🏛 License
|
## 🏛 License
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
import { Metadata } from './Metadata'
|
import { Metadata } from './Metadata'
|
||||||
import { Status } from './Status'
|
import { Status } from './Status'
|
||||||
|
|
||||||
|
export interface ServiceCustomParameter {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
label: string
|
||||||
|
required: boolean
|
||||||
|
options?: any
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceCustomParametersRequired {
|
||||||
|
userCustomParameters?: ServiceCustomParameter[]
|
||||||
|
algoCustomParameters?: ServiceCustomParameter[]
|
||||||
|
}
|
||||||
|
|
||||||
export type ServiceType = 'authorization' | 'metadata' | 'access' | 'compute'
|
export type ServiceType = 'authorization' | 'metadata' | 'access' | 'compute'
|
||||||
|
|
||||||
export interface ServiceCommonAttributes {
|
export interface ServiceCommonAttributes extends ServiceCustomParametersRequired {
|
||||||
main: { [key: string]: any }
|
main: { [key: string]: any }
|
||||||
additionalInformation?: { [key: string]: any }
|
additionalInformation?: { [key: string]: any }
|
||||||
status?: Status
|
status?: Status
|
||||||
@ -33,9 +47,9 @@ export interface publisherTrustedAlgorithm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ServiceComputePrivacy {
|
export interface ServiceComputePrivacy {
|
||||||
allowRawAlgorithm: boolean
|
allowRawAlgorithm?: boolean
|
||||||
allowNetworkAccess: boolean
|
allowNetworkAccess?: boolean
|
||||||
allowAllPublishedAlgorithms: boolean
|
allowAllPublishedAlgorithms?: boolean
|
||||||
publisherTrustedAlgorithms?: publisherTrustedAlgorithm[]
|
publisherTrustedAlgorithms?: publisherTrustedAlgorithm[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import { DDO } from '../ddo/DDO'
|
import { DDO } from '../ddo/DDO'
|
||||||
import { Metadata } from '../ddo/interfaces/Metadata'
|
import { Metadata } from '../ddo/interfaces/Metadata'
|
||||||
import { Service, ServiceAccess } from '../ddo/interfaces/Service'
|
import {
|
||||||
|
Service,
|
||||||
|
ServiceAccess,
|
||||||
|
ServiceCustomParameter,
|
||||||
|
ServiceCustomParametersRequired
|
||||||
|
} from '../ddo/interfaces/Service'
|
||||||
import { SearchQuery } from '../metadatacache/MetadataCache'
|
import { SearchQuery } from '../metadatacache/MetadataCache'
|
||||||
import { EditableMetadata } from '../ddo/interfaces/EditableMetadata'
|
import { EditableMetadata } from '../ddo/interfaces/EditableMetadata'
|
||||||
import Account from './Account'
|
import Account from './Account'
|
||||||
@ -9,15 +14,11 @@ import { SubscribablePromise, didNoZeroX, didPrefixed, assetResolve } from '../u
|
|||||||
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
||||||
import { WebServiceConnector } from './utils/WebServiceConnector'
|
import { WebServiceConnector } from './utils/WebServiceConnector'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import { Provider } from '../provider/Provider'
|
import { Provider, UserCustomParameters } from '../provider/Provider'
|
||||||
import { isAddress } from 'web3-utils'
|
import { isAddress } from 'web3-utils'
|
||||||
import { MetadataMain } from '../ddo/interfaces'
|
import { MetadataMain } from '../ddo/interfaces'
|
||||||
import { TransactionReceipt } from 'web3-core'
|
import { TransactionReceipt } from 'web3-core'
|
||||||
import {
|
import { CredentialType } from '../ddo/interfaces/Credentials'
|
||||||
CredentialType,
|
|
||||||
CredentialAction,
|
|
||||||
Credentials
|
|
||||||
} from '../ddo/interfaces/Credentials'
|
|
||||||
import { updateCredentialDetail, removeCredentialDetail } from './AssetsCredential'
|
import { updateCredentialDetail, removeCredentialDetail } from './AssetsCredential'
|
||||||
import { Consumable } from '../ddo/interfaces/Consumable'
|
import { Consumable } from '../ddo/interfaces/Consumable'
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ export class Assets extends Instantiable {
|
|||||||
* @param {String} name Token name
|
* @param {String} name Token name
|
||||||
* @param {String} symbol Token symbol
|
* @param {String} symbol Token symbol
|
||||||
* @param {String} providerUri
|
* @param {String} providerUri
|
||||||
* @return {Promise<DDO>}
|
* @return {SubscribablePromise<CreateProgressStep, DDO>}
|
||||||
*/
|
*/
|
||||||
public create(
|
public create(
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
@ -443,17 +444,20 @@ export class Assets extends Instantiable {
|
|||||||
* @param {String} cost number of datatokens needed for this service
|
* @param {String} cost number of datatokens needed for this service
|
||||||
* @param {String} datePublished
|
* @param {String} datePublished
|
||||||
* @param {Number} timeout
|
* @param {Number} timeout
|
||||||
* @return {Promise<string>} service
|
* @param {String} providerUri
|
||||||
|
* @param {ServiceCustomParametersRequired} requiredParameters
|
||||||
|
* @return {Promise<ServiceAccess>} service
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public async createAccessServiceAttributes(
|
public async createAccessServiceAttributes(
|
||||||
creator: Account,
|
creator: Account,
|
||||||
cost: string,
|
cost: string,
|
||||||
datePublished: string,
|
datePublished: string,
|
||||||
timeout = 0,
|
timeout: number = 0,
|
||||||
providerUri?: string
|
providerUri?: string,
|
||||||
|
requiredParameters?: ServiceCustomParametersRequired
|
||||||
): Promise<ServiceAccess> {
|
): Promise<ServiceAccess> {
|
||||||
return {
|
const service: ServiceAccess = {
|
||||||
type: 'access',
|
type: 'access',
|
||||||
index: 2,
|
index: 2,
|
||||||
serviceEndpoint: providerUri || this.ocean.provider.url,
|
serviceEndpoint: providerUri || this.ocean.provider.url,
|
||||||
@ -467,6 +471,11 @@ export class Assets extends Instantiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (requiredParameters?.userCustomParameters)
|
||||||
|
service.attributes.userCustomParameters = requiredParameters.userCustomParameters
|
||||||
|
if (requiredParameters?.algoCustomParameters)
|
||||||
|
service.attributes.algoCustomParameters = requiredParameters.algoCustomParameters
|
||||||
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -477,14 +486,16 @@ export class Assets extends Instantiable {
|
|||||||
* @param {String} consumerAddress
|
* @param {String} consumerAddress
|
||||||
* @param {Number} serviceIndex
|
* @param {Number} serviceIndex
|
||||||
* @param {String} serviceEndpoint
|
* @param {String} serviceEndpoint
|
||||||
|
* @param {UserCustomParameters} userCustomParameters
|
||||||
* @return {Promise<any>} Order details
|
* @return {Promise<any>} Order details
|
||||||
*/
|
*/
|
||||||
public async initialize(
|
public async initialize(
|
||||||
asset: DDO | string,
|
asset: DDO | string,
|
||||||
serviceType: string,
|
serviceType: string,
|
||||||
consumerAddress: string,
|
consumerAddress: string,
|
||||||
serviceIndex = -1,
|
serviceIndex: number = -1,
|
||||||
serviceEndpoint: string
|
serviceEndpoint: string,
|
||||||
|
userCustomParameters?: UserCustomParameters
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const provider = await Provider.getInstance(this.instanceConfig)
|
const provider = await Provider.getInstance(this.instanceConfig)
|
||||||
await provider.setBaseUrl(serviceEndpoint)
|
await provider.setBaseUrl(serviceEndpoint)
|
||||||
@ -492,7 +503,8 @@ export class Assets extends Instantiable {
|
|||||||
asset,
|
asset,
|
||||||
serviceIndex,
|
serviceIndex,
|
||||||
serviceType,
|
serviceType,
|
||||||
consumerAddress
|
consumerAddress,
|
||||||
|
userCustomParameters
|
||||||
)
|
)
|
||||||
if (res === null) return null
|
if (res === null) return null
|
||||||
const providerData = JSON.parse(res)
|
const providerData = JSON.parse(res)
|
||||||
@ -507,15 +519,17 @@ export class Assets extends Instantiable {
|
|||||||
* @param {Number} serviceIndex
|
* @param {Number} serviceIndex
|
||||||
* @param {String} mpAddress Marketplace fee collector address
|
* @param {String} mpAddress Marketplace fee collector address
|
||||||
* @param {String} consumerAddress Optionally, if the consumer is another address than payer
|
* @param {String} consumerAddress Optionally, if the consumer is another address than payer
|
||||||
|
* @param {UserCustomParameters} userCustomParameters
|
||||||
* @return {Promise<String>} transactionHash of the payment
|
* @return {Promise<String>} transactionHash of the payment
|
||||||
*/
|
*/
|
||||||
public async order(
|
public async order(
|
||||||
asset: DDO | string,
|
asset: DDO | string,
|
||||||
serviceType: string,
|
serviceType: string,
|
||||||
payerAddress: string,
|
payerAddress: string,
|
||||||
serviceIndex = -1,
|
serviceIndex: number = -1,
|
||||||
mpAddress?: string,
|
mpAddress?: string,
|
||||||
consumerAddress?: string,
|
consumerAddress?: string,
|
||||||
|
userCustomParameters?: UserCustomParameters,
|
||||||
searchPreviousOrders = true
|
searchPreviousOrders = true
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
let service: Service
|
let service: Service
|
||||||
@ -533,13 +547,25 @@ export class Assets extends Instantiable {
|
|||||||
service = await this.getServiceByIndex(ddo, serviceIndex)
|
service = await this.getServiceByIndex(ddo, serviceIndex)
|
||||||
serviceType = service.type
|
serviceType = service.type
|
||||||
}
|
}
|
||||||
|
// TODO validate userCustomParameters
|
||||||
|
if (
|
||||||
|
!(await this.isUserCustomParametersValid(
|
||||||
|
service.attributes.userCustomParameters,
|
||||||
|
userCustomParameters
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Order asset failed, Missing required fiels in userCustomParameters`
|
||||||
|
)
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const providerData = await this.initialize(
|
const providerData = await this.initialize(
|
||||||
ddo,
|
ddo,
|
||||||
serviceType,
|
serviceType,
|
||||||
payerAddress,
|
payerAddress,
|
||||||
serviceIndex,
|
serviceIndex,
|
||||||
service.serviceEndpoint
|
service.serviceEndpoint,
|
||||||
|
userCustomParameters
|
||||||
)
|
)
|
||||||
if (!providerData)
|
if (!providerData)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -732,4 +758,25 @@ export class Assets extends Instantiable {
|
|||||||
*/
|
*/
|
||||||
return { status, message, result }
|
return { status, message, result }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate custom user parameters (user & algorithms)
|
||||||
|
* @param {ServiceCustomParameter[]} serviceCustomParameters
|
||||||
|
* @param {UserCustomParameters} userCustomParameters
|
||||||
|
* @return {Promise<Boolean>}
|
||||||
|
*/
|
||||||
|
public async isUserCustomParametersValid(
|
||||||
|
serviceCustomParameters: ServiceCustomParameter[],
|
||||||
|
userCustomParameters?: UserCustomParameters
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (serviceCustomParameters)
|
||||||
|
for (const data of serviceCustomParameters) {
|
||||||
|
const keyname = data.name
|
||||||
|
if (!userCustomParameters || !userCustomParameters[keyname]) {
|
||||||
|
this.logger.error('Missing key: ' + keyname + ' from customData')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ import {
|
|||||||
Service,
|
Service,
|
||||||
ServiceComputePrivacy,
|
ServiceComputePrivacy,
|
||||||
ServiceCompute,
|
ServiceCompute,
|
||||||
publisherTrustedAlgorithm
|
publisherTrustedAlgorithm,
|
||||||
|
ServiceCustomParametersRequired
|
||||||
} from '../ddo/interfaces/Service'
|
} from '../ddo/interfaces/Service'
|
||||||
import Account from './Account'
|
import Account from './Account'
|
||||||
import { SubscribablePromise, assetResolve, AssetResolved } from '../utils'
|
import { SubscribablePromise, assetResolve, AssetResolved } from '../utils'
|
||||||
@ -14,7 +15,7 @@ import {
|
|||||||
ComputeInput,
|
ComputeInput,
|
||||||
ComputeAlgorithm
|
ComputeAlgorithm
|
||||||
} from './interfaces/Compute'
|
} from './interfaces/Compute'
|
||||||
import { Provider } from '../provider/Provider'
|
import { Provider, UserCustomParameters } from '../provider/Provider'
|
||||||
import { SHA256 } from 'crypto-js'
|
import { SHA256 } from 'crypto-js'
|
||||||
|
|
||||||
export enum OrderProgressStep {
|
export enum OrderProgressStep {
|
||||||
@ -121,6 +122,18 @@ export class Compute extends Instantiable {
|
|||||||
const { did, ddo } = await assetResolve(asset, this.ocean)
|
const { did, ddo } = await assetResolve(asset, this.ocean)
|
||||||
const service = ddo.findServiceByType('compute')
|
const service = ddo.findServiceByType('compute')
|
||||||
const { serviceEndpoint } = service
|
const { serviceEndpoint } = service
|
||||||
|
if (algorithm.serviceIndex) {
|
||||||
|
const { ddo } = await assetResolve(algorithm.did, this.ocean)
|
||||||
|
const algoService: Service = ddo.findServiceById(algorithm.serviceIndex)
|
||||||
|
if (
|
||||||
|
!(await this.ocean.assets.isUserCustomParametersValid(
|
||||||
|
algoService.attributes.algoCustomParameters,
|
||||||
|
algorithm.algoCustomParameters
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
if (did && txId) {
|
if (did && txId) {
|
||||||
const provider = await Provider.getInstance(this.instanceConfig)
|
const provider = await Provider.getInstance(this.instanceConfig)
|
||||||
await provider.setBaseUrl(serviceEndpoint)
|
await provider.setBaseUrl(serviceEndpoint)
|
||||||
@ -343,11 +356,12 @@ export class Compute extends Instantiable {
|
|||||||
providerAttributes: any,
|
providerAttributes: any,
|
||||||
computePrivacy?: ServiceComputePrivacy,
|
computePrivacy?: ServiceComputePrivacy,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
providerUri?: string
|
providerUri?: string,
|
||||||
|
requiredCustomParameters?: ServiceCustomParametersRequired
|
||||||
): ServiceCompute {
|
): ServiceCompute {
|
||||||
const name = 'dataAssetComputingService'
|
const name = 'dataAssetComputingService'
|
||||||
if (!timeout) timeout = 3600
|
if (!timeout) timeout = 3600
|
||||||
const service = {
|
const service: ServiceCompute = {
|
||||||
type: 'compute',
|
type: 'compute',
|
||||||
index: 3,
|
index: 3,
|
||||||
serviceEndpoint: providerUri || this.ocean.provider.url,
|
serviceEndpoint: providerUri || this.ocean.provider.url,
|
||||||
@ -365,6 +379,12 @@ export class Compute extends Instantiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (computePrivacy) service.attributes.main.privacy = computePrivacy
|
if (computePrivacy) service.attributes.main.privacy = computePrivacy
|
||||||
|
if (requiredCustomParameters?.userCustomParameters)
|
||||||
|
service.attributes.userCustomParameters =
|
||||||
|
requiredCustomParameters.userCustomParameters
|
||||||
|
if (requiredCustomParameters?.algoCustomParameters)
|
||||||
|
service.attributes.algoCustomParameters =
|
||||||
|
requiredCustomParameters.algoCustomParameters
|
||||||
return service as ServiceCompute
|
return service as ServiceCompute
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,7 +528,7 @@ export class Compute extends Instantiable {
|
|||||||
* @param {string} algorithmDid The DID of the algorithm asset (of type `algorithm`) to run on the asset.
|
* @param {string} algorithmDid The DID of the algorithm asset (of type `algorithm`) to run on the asset.
|
||||||
* @param {string} algorithmServiceIndex The index of the service in the algorithm
|
* @param {string} algorithmServiceIndex The index of the service in the algorithm
|
||||||
* @param {MetaData} algorithmMeta Metadata about the algorithm being run if `algorithm` is being used. This is ignored when `algorithmDid` is specified.
|
* @param {MetaData} algorithmMeta Metadata about the algorithm being run if `algorithm` is being used. This is ignored when `algorithmDid` is specified.
|
||||||
* @return {Promise<string>} Returns the transaction details
|
* @return {SubscribablePromise<OrderProgressStep, string>} Returns the transaction details
|
||||||
*
|
*
|
||||||
* Note: algorithmDid and algorithmMeta are optional, but if they are not passed,
|
* 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 compute job,
|
* you can end up in the situation that you are ordering and paying for your compute job,
|
||||||
@ -521,6 +541,7 @@ export class Compute extends Instantiable {
|
|||||||
algorithm: ComputeAlgorithm,
|
algorithm: ComputeAlgorithm,
|
||||||
mpAddress?: string,
|
mpAddress?: string,
|
||||||
computeAddress?: string,
|
computeAddress?: string,
|
||||||
|
userCustomParameters?: UserCustomParameters,
|
||||||
searchPreviousOrders = true
|
searchPreviousOrders = true
|
||||||
): SubscribablePromise<OrderProgressStep, string> {
|
): SubscribablePromise<OrderProgressStep, string> {
|
||||||
return new SubscribablePromise(async (observer) => {
|
return new SubscribablePromise(async (observer) => {
|
||||||
@ -543,6 +564,7 @@ export class Compute extends Instantiable {
|
|||||||
-1,
|
-1,
|
||||||
mpAddress,
|
mpAddress,
|
||||||
computeAddress,
|
computeAddress,
|
||||||
|
userCustomParameters,
|
||||||
searchPreviousOrders
|
searchPreviousOrders
|
||||||
)
|
)
|
||||||
return order
|
return order
|
||||||
@ -570,6 +592,7 @@ export class Compute extends Instantiable {
|
|||||||
serviceIndex = -1,
|
serviceIndex = -1,
|
||||||
mpAddress?: string,
|
mpAddress?: string,
|
||||||
consumerAddress?: string,
|
consumerAddress?: string,
|
||||||
|
userCustomParameters?: UserCustomParameters,
|
||||||
searchPreviousOrders = true
|
searchPreviousOrders = true
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
// this is only a convienince function, which calls ocean.assets.order
|
// this is only a convienince function, which calls ocean.assets.order
|
||||||
@ -581,6 +604,7 @@ export class Compute extends Instantiable {
|
|||||||
serviceIndex,
|
serviceIndex,
|
||||||
mpAddress,
|
mpAddress,
|
||||||
consumerAddress,
|
consumerAddress,
|
||||||
|
userCustomParameters,
|
||||||
searchPreviousOrders
|
searchPreviousOrders
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -43,4 +43,5 @@ export interface ComputeAlgorithm {
|
|||||||
meta?: MetadataAlgorithm
|
meta?: MetadataAlgorithm
|
||||||
transferTxId?: string
|
transferTxId?: string
|
||||||
dataToken?: string
|
dataToken?: string
|
||||||
|
algoCustomParameters?: { [key: string]: any }
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,10 @@ export interface ServiceEndpoint {
|
|||||||
urlPath: string
|
urlPath: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserCustomParameters {
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an interface for provider service.
|
* Provides an interface for provider service.
|
||||||
* Provider service is the technical component executed
|
* Provider service is the technical component executed
|
||||||
@ -188,7 +192,8 @@ export class Provider extends Instantiable {
|
|||||||
asset: DDO | string,
|
asset: DDO | string,
|
||||||
serviceIndex: number,
|
serviceIndex: number,
|
||||||
serviceType: string,
|
serviceType: string,
|
||||||
consumerAddress: string
|
consumerAddress: string,
|
||||||
|
userCustomParameters?: UserCustomParameters
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const { did, ddo } = await assetResolve(asset, this.ocean)
|
const { did, ddo } = await assetResolve(asset, this.ocean)
|
||||||
let initializeUrl = this.getInitializeEndpoint()
|
let initializeUrl = this.getInitializeEndpoint()
|
||||||
@ -200,6 +205,8 @@ export class Provider extends Instantiable {
|
|||||||
initializeUrl += `&serviceType=${serviceType}`
|
initializeUrl += `&serviceType=${serviceType}`
|
||||||
initializeUrl += `&dataToken=${ddo.dataToken}`
|
initializeUrl += `&dataToken=${ddo.dataToken}`
|
||||||
initializeUrl += `&consumerAddress=${consumerAddress}`
|
initializeUrl += `&consumerAddress=${consumerAddress}`
|
||||||
|
if (userCustomParameters)
|
||||||
|
initializeUrl += '&userdata=' + encodeURI(JSON.stringify(userCustomParameters))
|
||||||
try {
|
try {
|
||||||
const response = await this.ocean.utils.fetch.get(initializeUrl)
|
const response = await this.ocean.utils.fetch.get(initializeUrl)
|
||||||
return await response.text()
|
return await response.text()
|
||||||
@ -218,7 +225,8 @@ export class Provider extends Instantiable {
|
|||||||
destination: string,
|
destination: string,
|
||||||
account: Account,
|
account: Account,
|
||||||
files: File[],
|
files: File[],
|
||||||
index = -1
|
index = -1,
|
||||||
|
userCustomParameters?: UserCustomParameters
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
await this.getNonce(account.getId())
|
await this.getNonce(account.getId())
|
||||||
const signature = await this.createSignature(account, did + this.nonce)
|
const signature = await this.createSignature(account, did + this.nonce)
|
||||||
@ -236,7 +244,8 @@ export class Provider extends Instantiable {
|
|||||||
consumeUrl += `&transferTxId=${txId}`
|
consumeUrl += `&transferTxId=${txId}`
|
||||||
consumeUrl += `&consumerAddress=${account.getId()}`
|
consumeUrl += `&consumerAddress=${account.getId()}`
|
||||||
consumeUrl += `&signature=${signature}`
|
consumeUrl += `&signature=${signature}`
|
||||||
|
if (userCustomParameters)
|
||||||
|
consumeUrl += '&userdata=' + encodeURI(JSON.stringify(userCustomParameters))
|
||||||
try {
|
try {
|
||||||
!destination
|
!destination
|
||||||
? await this.ocean.utils.fetch.downloadFileBrowser(consumeUrl)
|
? await this.ocean.utils.fetch.downloadFileBrowser(consumeUrl)
|
||||||
@ -262,7 +271,8 @@ export class Provider extends Instantiable {
|
|||||||
serviceIndex?: string,
|
serviceIndex?: string,
|
||||||
serviceType?: string,
|
serviceType?: string,
|
||||||
tokenAddress?: string,
|
tokenAddress?: string,
|
||||||
additionalInputs?: ComputeInput[]
|
additionalInputs?: ComputeInput[],
|
||||||
|
userCustomParameters?: UserCustomParameters
|
||||||
): Promise<ComputeJob | ComputeJob[]> {
|
): Promise<ComputeJob | ComputeJob[]> {
|
||||||
const address = consumerAccount.getId()
|
const address = consumerAccount.getId()
|
||||||
await this.getNonce(consumerAccount.getId())
|
await this.getNonce(consumerAccount.getId())
|
||||||
@ -291,6 +301,9 @@ export class Provider extends Instantiable {
|
|||||||
if (tokenAddress) payload.dataToken = tokenAddress
|
if (tokenAddress) payload.dataToken = tokenAddress
|
||||||
|
|
||||||
if (additionalInputs) payload.additionalInputs = additionalInputs
|
if (additionalInputs) payload.additionalInputs = additionalInputs
|
||||||
|
if (userCustomParameters) payload.userData = userCustomParameters
|
||||||
|
if (algorithm.algoCustomParameters)
|
||||||
|
payload.algouserdata = algorithm.algoCustomParameters
|
||||||
const path = this.getComputeStartEndpoint()
|
const path = this.getComputeStartEndpoint()
|
||||||
? this.getComputeStartEndpoint().urlPath
|
? this.getComputeStartEndpoint().urlPath
|
||||||
: null
|
: null
|
||||||
|
@ -42,6 +42,7 @@ describe('Compute flow', () => {
|
|||||||
let algorithmAssetwithCompute: DDO
|
let algorithmAssetwithCompute: DDO
|
||||||
let algorithmAssetRemoteProvider: DDO
|
let algorithmAssetRemoteProvider: DDO
|
||||||
let algorithmAssetRemoteProviderWithCompute: DDO
|
let algorithmAssetRemoteProviderWithCompute: DDO
|
||||||
|
let algorithmAssetWithCustomData: DDO
|
||||||
let contracts: TestContractHandler
|
let contracts: TestContractHandler
|
||||||
let datatoken: DataTokens
|
let datatoken: DataTokens
|
||||||
let tokenAddress: string
|
let tokenAddress: string
|
||||||
@ -54,6 +55,7 @@ describe('Compute flow', () => {
|
|||||||
let tokenAddressAlgorithmRemoteProviderWithCompute: string
|
let tokenAddressAlgorithmRemoteProviderWithCompute: string
|
||||||
let tokenAddressAdditional1: string
|
let tokenAddressAdditional1: string
|
||||||
let tokenAddressAdditional2: string
|
let tokenAddressAdditional2: string
|
||||||
|
let tokenAddressWithCustomData: string
|
||||||
let price: string
|
let price: string
|
||||||
let ocean: Ocean
|
let ocean: Ocean
|
||||||
let data: { t: number; url: string }
|
let data: { t: number; url: string }
|
||||||
@ -209,6 +211,18 @@ describe('Compute flow', () => {
|
|||||||
'Add2'
|
'Add2'
|
||||||
)
|
)
|
||||||
assert(tokenAddressAdditional2 != null, 'Creation of tokenAddressAdditional2 failed')
|
assert(tokenAddressAdditional2 != null, 'Creation of tokenAddressAdditional2 failed')
|
||||||
|
|
||||||
|
tokenAddressWithCustomData = await datatoken.create(
|
||||||
|
blob,
|
||||||
|
alice.getId(),
|
||||||
|
'10000000000',
|
||||||
|
'WCD',
|
||||||
|
'WCD'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
tokenAddressWithCustomData != null,
|
||||||
|
'Creation of tokenAddressWithCustomData failed'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Generates metadata', async () => {
|
it('Generates metadata', async () => {
|
||||||
@ -704,6 +718,95 @@ describe('Compute flow', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should publish an algorithm whith CustomData', async () => {
|
||||||
|
const assetWithCustomData: Metadata = {
|
||||||
|
main: {
|
||||||
|
type: 'algorithm',
|
||||||
|
name: 'Test Algo with CustomData',
|
||||||
|
dateCreated: dateCreated,
|
||||||
|
datePublished: dateCreated,
|
||||||
|
author: 'DevOps',
|
||||||
|
license: 'CC-BY',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
url: 'https://raw.githubusercontent.com/oceanprotocol/test-algorithm/master/javascript/algo.js',
|
||||||
|
contentType: 'text/js',
|
||||||
|
encoding: 'UTF-8'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
algorithm: {
|
||||||
|
language: 'js',
|
||||||
|
format: 'docker-image',
|
||||||
|
version: '0.1',
|
||||||
|
container: {
|
||||||
|
entrypoint: 'node $ALGO',
|
||||||
|
image: 'node',
|
||||||
|
tag: '10'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const customdata = {
|
||||||
|
userCustomParameters: [
|
||||||
|
{
|
||||||
|
name: 'firstname',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Your first name',
|
||||||
|
required: true,
|
||||||
|
description: 'Your name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lastname',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Your last name',
|
||||||
|
required: false,
|
||||||
|
description: 'Your last name'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
algoCustomParameters: [
|
||||||
|
{
|
||||||
|
name: 'iterations',
|
||||||
|
type: 'number',
|
||||||
|
label: 'Iterations',
|
||||||
|
required: true,
|
||||||
|
description: 'No of passes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'chunk',
|
||||||
|
type: 'number',
|
||||||
|
label: 'Chunks',
|
||||||
|
required: false,
|
||||||
|
description: 'No of chunks'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
const service1 = await ocean.assets.createAccessServiceAttributes(
|
||||||
|
alice,
|
||||||
|
price,
|
||||||
|
dateCreated,
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
customdata
|
||||||
|
)
|
||||||
|
algorithmAssetWithCustomData = await ocean.assets.create(
|
||||||
|
assetWithCustomData,
|
||||||
|
alice,
|
||||||
|
[service1],
|
||||||
|
tokenAddressWithCustomData
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
algorithmAssetWithCustomData.dataToken === tokenAddressWithCustomData,
|
||||||
|
'algorithmAssetWithCustomData.dataToken !== tokenAddressWithCustomData'
|
||||||
|
)
|
||||||
|
const storeTx = await ocean.onChainMetadata.publish(
|
||||||
|
algorithmAssetWithCustomData.id,
|
||||||
|
algorithmAssetWithCustomData,
|
||||||
|
alice.getId()
|
||||||
|
)
|
||||||
|
assert(storeTx)
|
||||||
|
await ocean.metadataCache.waitForAqua(algorithmAssetWithCustomData.id)
|
||||||
|
})
|
||||||
|
|
||||||
it('Alice mints 100 DTs and tranfers them to the compute marketplace', async () => {
|
it('Alice mints 100 DTs and tranfers them to the compute marketplace', async () => {
|
||||||
await datatoken.mint(tokenAddress, alice.getId(), tokenAmount)
|
await datatoken.mint(tokenAddress, alice.getId(), tokenAmount)
|
||||||
await datatoken.mint(tokenAddressNoRawAlgo, alice.getId(), tokenAmount)
|
await datatoken.mint(tokenAddressNoRawAlgo, alice.getId(), tokenAmount)
|
||||||
@ -719,90 +822,98 @@ describe('Compute flow', () => {
|
|||||||
)
|
)
|
||||||
await datatoken.mint(tokenAddressAdditional1, alice.getId(), tokenAmount)
|
await datatoken.mint(tokenAddressAdditional1, alice.getId(), tokenAmount)
|
||||||
await datatoken.mint(tokenAddressAdditional2, alice.getId(), tokenAmount)
|
await datatoken.mint(tokenAddressAdditional2, alice.getId(), tokenAmount)
|
||||||
|
await datatoken.mint(tokenAddressWithCustomData, alice.getId(), tokenAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Bob gets datatokens from Alice to be able to try the compute service', async () => {
|
it('Bob gets datatokens from Alice to be able to try the compute service', async () => {
|
||||||
|
let balance
|
||||||
const dTamount = '200'
|
const dTamount = '200'
|
||||||
await datatoken
|
await datatoken.transfer(tokenAddress, bob.getId(), dTamount, alice.getId())
|
||||||
.transfer(tokenAddress, bob.getId(), dTamount, alice.getId())
|
balance = await datatoken.balance(tokenAddress, bob.getId())
|
||||||
.then(async () => {
|
assert(balance.toString() === dTamount.toString())
|
||||||
const balance = await datatoken.balance(tokenAddress, bob.getId())
|
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
|
||||||
await datatoken
|
|
||||||
.transfer(tokenAddressNoRawAlgo, bob.getId(), dTamount, alice.getId())
|
|
||||||
.then(async () => {
|
|
||||||
const balance = await datatoken.balance(tokenAddressNoRawAlgo, bob.getId())
|
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
|
||||||
await datatoken
|
|
||||||
.transfer(tokenAddressWithTrustedAlgo, bob.getId(), dTamount, alice.getId())
|
|
||||||
.then(async () => {
|
|
||||||
const balance = await datatoken.balance(tokenAddressWithTrustedAlgo, bob.getId())
|
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
|
||||||
|
|
||||||
await datatoken
|
await datatoken.transfer(tokenAddressNoRawAlgo, bob.getId(), dTamount, alice.getId())
|
||||||
.transfer(tokenAddressWithBogusProvider, bob.getId(), dTamount, alice.getId())
|
balance = await datatoken.balance(tokenAddressNoRawAlgo, bob.getId())
|
||||||
.then(async () => {
|
assert(balance.toString() === dTamount.toString())
|
||||||
const balance = await datatoken.balance(
|
|
||||||
tokenAddressWithBogusProvider,
|
|
||||||
bob.getId()
|
|
||||||
)
|
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
|
||||||
await datatoken
|
|
||||||
.transfer(tokenAddressAlgorithm, bob.getId(), dTamount, alice.getId())
|
|
||||||
.then(async () => {
|
|
||||||
const balance = await datatoken.balance(tokenAddressAlgorithm, bob.getId())
|
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
|
||||||
await datatoken
|
|
||||||
.transfer(tokenAddressAlgorithmwithCompute, bob.getId(), dTamount, alice.getId())
|
|
||||||
.then(async () => {
|
|
||||||
const balance = await datatoken.balance(
|
|
||||||
tokenAddressAlgorithmwithCompute,
|
|
||||||
bob.getId()
|
|
||||||
)
|
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
|
||||||
await datatoken
|
|
||||||
.transfer(tokenAddressAlgorithmRemoteProvider, bob.getId(), dTamount, alice.getId())
|
|
||||||
.then(async () => {
|
|
||||||
const balance = await datatoken.balance(
|
|
||||||
tokenAddressAlgorithmRemoteProvider,
|
|
||||||
bob.getId()
|
|
||||||
)
|
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
|
||||||
await datatoken
|
|
||||||
.transfer(
|
|
||||||
tokenAddressAlgorithmRemoteProviderWithCompute,
|
|
||||||
bob.getId(),
|
|
||||||
dTamount,
|
|
||||||
alice.getId()
|
|
||||||
)
|
|
||||||
.then(async () => {
|
|
||||||
const balance = await datatoken.balance(
|
|
||||||
tokenAddressAlgorithmRemoteProviderWithCompute,
|
|
||||||
bob.getId()
|
|
||||||
)
|
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
|
||||||
|
|
||||||
await datatoken
|
await datatoken.transfer(
|
||||||
.transfer(tokenAddressAdditional1, bob.getId(), dTamount, alice.getId())
|
tokenAddressWithTrustedAlgo,
|
||||||
.then(async () => {
|
bob.getId(),
|
||||||
const balance = await datatoken.balance(tokenAddressAdditional1, bob.getId())
|
dTamount,
|
||||||
assert(balance.toString() === dTamount.toString())
|
alice.getId()
|
||||||
})
|
)
|
||||||
|
balance = await datatoken.balance(tokenAddressWithTrustedAlgo, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
await datatoken
|
await datatoken.transfer(
|
||||||
.transfer(tokenAddressAdditional2, bob.getId(), dTamount, alice.getId())
|
tokenAddressWithBogusProvider,
|
||||||
.then(async () => {
|
bob.getId(),
|
||||||
const balance = await datatoken.balance(tokenAddressAdditional2, bob.getId())
|
dTamount,
|
||||||
assert(balance.toString() === dTamount.toString())
|
alice.getId()
|
||||||
})
|
)
|
||||||
|
balance = await datatoken.balance(tokenAddressWithBogusProvider, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
|
await datatoken.transfer(tokenAddressAlgorithm, bob.getId(), dTamount, alice.getId())
|
||||||
|
balance = await datatoken.balance(tokenAddressAlgorithm, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
|
await datatoken.transfer(
|
||||||
|
tokenAddressAlgorithmwithCompute,
|
||||||
|
bob.getId(),
|
||||||
|
dTamount,
|
||||||
|
alice.getId()
|
||||||
|
)
|
||||||
|
balance = await datatoken.balance(tokenAddressAlgorithmwithCompute, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
|
await datatoken.transfer(
|
||||||
|
tokenAddressAlgorithmRemoteProvider,
|
||||||
|
bob.getId(),
|
||||||
|
dTamount,
|
||||||
|
alice.getId()
|
||||||
|
)
|
||||||
|
balance = await datatoken.balance(tokenAddressAlgorithmRemoteProvider, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
|
await datatoken.transfer(
|
||||||
|
tokenAddressAlgorithmRemoteProviderWithCompute,
|
||||||
|
bob.getId(),
|
||||||
|
dTamount,
|
||||||
|
alice.getId()
|
||||||
|
)
|
||||||
|
balance = await datatoken.balance(
|
||||||
|
tokenAddressAlgorithmRemoteProviderWithCompute,
|
||||||
|
bob.getId()
|
||||||
|
)
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
|
await datatoken.transfer(
|
||||||
|
tokenAddressAdditional1,
|
||||||
|
bob.getId(),
|
||||||
|
dTamount,
|
||||||
|
alice.getId()
|
||||||
|
)
|
||||||
|
balance = await datatoken.balance(tokenAddressAdditional1, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
|
await datatoken.transfer(
|
||||||
|
tokenAddressAdditional2,
|
||||||
|
bob.getId(),
|
||||||
|
dTamount,
|
||||||
|
alice.getId()
|
||||||
|
)
|
||||||
|
balance = await datatoken.balance(tokenAddressAdditional2, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
|
await datatoken.transfer(
|
||||||
|
tokenAddressWithCustomData,
|
||||||
|
bob.getId(),
|
||||||
|
dTamount,
|
||||||
|
alice.getId()
|
||||||
|
)
|
||||||
|
balance = await datatoken.balance(tokenAddressWithCustomData, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Bob starts compute job with a raw Algo', async () => {
|
it('Bob starts compute job with a raw Algo', async () => {
|
||||||
@ -1370,7 +1481,176 @@ describe('Compute flow', () => {
|
|||||||
assert(response.status >= 1, 'Invalid response.status')
|
assert(response.status >= 1, 'Invalid response.status')
|
||||||
assert(response.jobId, 'Invalid jobId')
|
assert(response.jobId, 'Invalid jobId')
|
||||||
})
|
})
|
||||||
|
it('should not be able start a compute job with a published algo that requires userdata without providing that', async () => {
|
||||||
|
const computeService = ddo.findServiceByType('compute')
|
||||||
|
assert(algorithmAssetWithCustomData != null, 'algorithmAsset should not be null')
|
||||||
|
const serviceAlgo = algorithmAssetWithCustomData.findServiceByType('access')
|
||||||
|
// get the compute address first
|
||||||
|
computeAddress = await ocean.compute.getComputeAddress(ddo.id, computeService.index)
|
||||||
|
assert(ddo != null, 'ddo should not be null')
|
||||||
|
const algoDefinition: ComputeAlgorithm = {
|
||||||
|
did: algorithmAssetWithCustomData.id,
|
||||||
|
serviceIndex: serviceAlgo.index
|
||||||
|
}
|
||||||
|
// check if asset is orderable. otherwise, you might pay for it, but it has some algo restrictions
|
||||||
|
const allowed = await ocean.compute.isOrderable(
|
||||||
|
ddo,
|
||||||
|
computeService.index,
|
||||||
|
algoDefinition,
|
||||||
|
algorithmAssetWithCustomData
|
||||||
|
)
|
||||||
|
assert(allowed === true)
|
||||||
|
const order = await ocean.compute.orderAsset(
|
||||||
|
bob.getId(),
|
||||||
|
ddo,
|
||||||
|
computeService.index,
|
||||||
|
algoDefinition,
|
||||||
|
null, // no marketplace fee
|
||||||
|
computeAddress // CtD is the consumer of the dataset
|
||||||
|
)
|
||||||
|
assert(order != null, 'Order should not be null')
|
||||||
|
// order the algorithm, without providing userdata
|
||||||
|
try {
|
||||||
|
const orderalgo = await ocean.compute.orderAlgorithm(
|
||||||
|
algorithmAssetWithCustomData,
|
||||||
|
serviceAlgo.type,
|
||||||
|
bob.getId(),
|
||||||
|
serviceAlgo.index,
|
||||||
|
null, // no marketplace fee
|
||||||
|
computeAddress // CtD is the consumer of the dataset
|
||||||
|
)
|
||||||
|
assert(orderalgo === null, 'Order should be null')
|
||||||
|
} catch (error) {
|
||||||
|
assert(error != null, 'Order should throw error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not be able to start a compute job with a published algo that requires algodata without providing that', async () => {
|
||||||
|
const output = {}
|
||||||
|
const computeService = ddo.findServiceByType('compute')
|
||||||
|
assert(algorithmAssetWithCustomData != null, 'algorithmAsset should not be null')
|
||||||
|
const serviceAlgo = algorithmAssetWithCustomData.findServiceByType('access')
|
||||||
|
// get the compute address first
|
||||||
|
computeAddress = await ocean.compute.getComputeAddress(ddo.id, computeService.index)
|
||||||
|
assert(ddo != null, 'ddo should not be null')
|
||||||
|
const algoDefinition: ComputeAlgorithm = {
|
||||||
|
did: algorithmAssetWithCustomData.id,
|
||||||
|
serviceIndex: serviceAlgo.index
|
||||||
|
}
|
||||||
|
// check if asset is orderable. otherwise, you might pay for it, but it has some algo restrictions
|
||||||
|
const allowed = await ocean.compute.isOrderable(
|
||||||
|
ddo,
|
||||||
|
computeService.index,
|
||||||
|
algoDefinition,
|
||||||
|
algorithmAssetWithCustomData
|
||||||
|
)
|
||||||
|
assert(allowed === true)
|
||||||
|
const bobUserData = {
|
||||||
|
firstname: 'Bob',
|
||||||
|
lastname: 'Doe'
|
||||||
|
}
|
||||||
|
const order = await ocean.compute.orderAsset(
|
||||||
|
bob.getId(),
|
||||||
|
ddo,
|
||||||
|
computeService.index,
|
||||||
|
algoDefinition,
|
||||||
|
null, // no marketplace fee
|
||||||
|
computeAddress // CtD is the consumer of the dataset
|
||||||
|
)
|
||||||
|
assert(order != null, 'Order should not be null')
|
||||||
|
|
||||||
|
const orderalgo = await ocean.compute.orderAlgorithm(
|
||||||
|
algorithmAssetWithCustomData,
|
||||||
|
serviceAlgo.type,
|
||||||
|
bob.getId(),
|
||||||
|
serviceAlgo.index,
|
||||||
|
null, // no marketplace fee
|
||||||
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
bobUserData
|
||||||
|
)
|
||||||
|
assert(orderalgo != null, 'Order should be null')
|
||||||
|
algoDefinition.transferTxId = orderalgo
|
||||||
|
algoDefinition.dataToken = algorithmAsset.dataToken
|
||||||
|
const response = await ocean.compute.start(
|
||||||
|
ddo,
|
||||||
|
order,
|
||||||
|
tokenAddress,
|
||||||
|
bob,
|
||||||
|
algoDefinition,
|
||||||
|
output,
|
||||||
|
`${computeService.index}`,
|
||||||
|
computeService.type,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
assert(response === null, 'Compute should not start')
|
||||||
|
})
|
||||||
|
it('should be able to start a compute job with a published algo that requires algodata by providing that', async () => {
|
||||||
|
const output = {}
|
||||||
|
const computeService = ddo.findServiceByType('compute')
|
||||||
|
assert(algorithmAssetWithCustomData != null, 'algorithmAsset should not be null')
|
||||||
|
const serviceAlgo = algorithmAssetWithCustomData.findServiceByType('access')
|
||||||
|
// get the compute address first
|
||||||
|
computeAddress = await ocean.compute.getComputeAddress(ddo.id, computeService.index)
|
||||||
|
assert(ddo != null, 'ddo should not be null')
|
||||||
|
const algoDefinition: ComputeAlgorithm = {
|
||||||
|
did: algorithmAssetWithCustomData.id,
|
||||||
|
serviceIndex: serviceAlgo.index,
|
||||||
|
algoCustomParameters: {
|
||||||
|
iterations: 20,
|
||||||
|
chunk: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check if asset is orderable. otherwise, you might pay for it, but it has some algo restrictions
|
||||||
|
const allowed = await ocean.compute.isOrderable(
|
||||||
|
ddo,
|
||||||
|
computeService.index,
|
||||||
|
algoDefinition,
|
||||||
|
algorithmAssetWithCustomData
|
||||||
|
)
|
||||||
|
assert(allowed === true)
|
||||||
|
const bobUserData = {
|
||||||
|
firstname: 'Bob',
|
||||||
|
lastname: 'Doe'
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = await ocean.compute.orderAsset(
|
||||||
|
bob.getId(),
|
||||||
|
ddo,
|
||||||
|
computeService.index,
|
||||||
|
algoDefinition,
|
||||||
|
null, // no marketplace fee
|
||||||
|
computeAddress // CtD is the consumer of the dataset
|
||||||
|
)
|
||||||
|
assert(order != null, 'Order should not be null')
|
||||||
|
|
||||||
|
const orderalgo = await ocean.compute.orderAlgorithm(
|
||||||
|
algorithmAssetWithCustomData,
|
||||||
|
serviceAlgo.type,
|
||||||
|
bob.getId(),
|
||||||
|
serviceAlgo.index,
|
||||||
|
null, // no marketplace fee
|
||||||
|
computeAddress, // CtD is the consumer of the dataset
|
||||||
|
bobUserData
|
||||||
|
)
|
||||||
|
assert(orderalgo != null, 'Order should be null')
|
||||||
|
algoDefinition.transferTxId = orderalgo
|
||||||
|
algoDefinition.dataToken = algorithmAssetWithCustomData.dataToken
|
||||||
|
const response = await ocean.compute.start(
|
||||||
|
ddo,
|
||||||
|
order,
|
||||||
|
tokenAddress,
|
||||||
|
bob,
|
||||||
|
algoDefinition,
|
||||||
|
output,
|
||||||
|
`${computeService.index}`,
|
||||||
|
computeService.type,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
assert(response, 'Compute error')
|
||||||
|
jobId = response.jobId
|
||||||
|
assert(response.status >= 1, 'Invalid response status')
|
||||||
|
assert(response.jobId, 'Invalid jobId')
|
||||||
|
})
|
||||||
it('Alice updates Compute Privacy, allowing some published algos', async () => {
|
it('Alice updates Compute Privacy, allowing some published algos', async () => {
|
||||||
const computeService = ddo.findServiceByType('compute')
|
const computeService = ddo.findServiceByType('compute')
|
||||||
assert(computeService, 'ComputeIndex should be >0')
|
assert(computeService, 'ComputeIndex should be >0')
|
||||||
|
@ -35,11 +35,13 @@ describe('Marketplace flow', () => {
|
|||||||
let ddoWithCredentialsAllowList
|
let ddoWithCredentialsAllowList
|
||||||
let ddoWithCredentialsDenyList
|
let ddoWithCredentialsDenyList
|
||||||
let ddoWithCredentials
|
let ddoWithCredentials
|
||||||
|
let ddoWithUserData
|
||||||
let asset
|
let asset
|
||||||
let assetWithPool
|
let assetWithPool
|
||||||
let assetWithBadUrl
|
let assetWithBadUrl
|
||||||
let assetWithEncrypt
|
let assetWithEncrypt
|
||||||
let assetInvalidNoName
|
let assetInvalidNoName
|
||||||
|
let assetWithUserData
|
||||||
let marketplace: Account
|
let marketplace: Account
|
||||||
let contracts: TestContractHandler
|
let contracts: TestContractHandler
|
||||||
let datatoken: DataTokens
|
let datatoken: DataTokens
|
||||||
@ -48,6 +50,7 @@ describe('Marketplace flow', () => {
|
|||||||
let tokenAddressForBadUrlAsset: string
|
let tokenAddressForBadUrlAsset: string
|
||||||
let tokenAddressEncrypted: string
|
let tokenAddressEncrypted: string
|
||||||
let tokenAddressInvalidNoName: string
|
let tokenAddressInvalidNoName: string
|
||||||
|
let tokenAddressWithUserData
|
||||||
let service1: ServiceAccess
|
let service1: ServiceAccess
|
||||||
let price: string
|
let price: string
|
||||||
let ocean: Ocean
|
let ocean: Ocean
|
||||||
@ -133,6 +136,15 @@ describe('Marketplace flow', () => {
|
|||||||
'DTA'
|
'DTA'
|
||||||
)
|
)
|
||||||
assert(tokenAddressInvalidNoName != null)
|
assert(tokenAddressInvalidNoName != null)
|
||||||
|
|
||||||
|
tokenAddressWithUserData = await datatoken.create(
|
||||||
|
blob,
|
||||||
|
alice.getId(),
|
||||||
|
'10000000000',
|
||||||
|
'AliceDT',
|
||||||
|
'DTA'
|
||||||
|
)
|
||||||
|
assert(tokenAddressWithUserData != null)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Generates metadata', async () => {
|
it('Generates metadata', async () => {
|
||||||
@ -235,6 +247,27 @@ describe('Marketplace flow', () => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assetWithUserData = {
|
||||||
|
main: {
|
||||||
|
type: 'dataset',
|
||||||
|
name: 'test-dataset-with-pools',
|
||||||
|
dateCreated: new Date(Date.now()).toISOString().split('.')[0] + 'Z', // remove milliseconds
|
||||||
|
datePublished: new Date(Date.now()).toISOString().split('.')[0] + 'Z', // remove milliseconds
|
||||||
|
author: 'oceanprotocol-team',
|
||||||
|
license: 'MIT',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
url: 'https://s3.amazonaws.com/testfiles.oceanprotocol.com/info.0.json',
|
||||||
|
checksum: 'efb2c764274b745f5fc37f97c6b0e761',
|
||||||
|
contentLength: '4535431',
|
||||||
|
contentType: 'text/csv',
|
||||||
|
encoding: 'UTF-8',
|
||||||
|
compression: 'zip'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
it('Should validate local metadata', async () => {
|
it('Should validate local metadata', async () => {
|
||||||
const valid = await ocean.metadataCache.validateMetadata(asset)
|
const valid = await ocean.metadataCache.validateMetadata(asset)
|
||||||
@ -342,6 +375,46 @@ describe('Marketplace flow', () => {
|
|||||||
false
|
false
|
||||||
)
|
)
|
||||||
assert(storeTxWithCredentials)
|
assert(storeTxWithCredentials)
|
||||||
|
|
||||||
|
const userdata = {
|
||||||
|
userCustomParameters: [
|
||||||
|
{
|
||||||
|
name: 'firstname',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Your first name',
|
||||||
|
required: true,
|
||||||
|
description: 'Your name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lastname',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Your last name',
|
||||||
|
required: false,
|
||||||
|
description: 'Your last name'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
const serviceWithUserData = await ocean.assets.createAccessServiceAttributes(
|
||||||
|
alice,
|
||||||
|
price,
|
||||||
|
publishedDate,
|
||||||
|
timeout,
|
||||||
|
null,
|
||||||
|
userdata
|
||||||
|
)
|
||||||
|
ddoWithUserData = await ocean.assets.create(
|
||||||
|
asset,
|
||||||
|
alice,
|
||||||
|
[serviceWithUserData],
|
||||||
|
tokenAddressWithUserData
|
||||||
|
)
|
||||||
|
const storeTxWithUserData = await ocean.onChainMetadata.publish(
|
||||||
|
ddoWithUserData.id,
|
||||||
|
ddoWithUserData,
|
||||||
|
alice.getId(),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
assert(storeTxWithUserData)
|
||||||
// wait for all this assets to be published
|
// wait for all this assets to be published
|
||||||
await ocean.metadataCache.waitForAqua(ddo.id)
|
await ocean.metadataCache.waitForAqua(ddo.id)
|
||||||
await ocean.metadataCache.waitForAqua(ddoWithBadUrl.id)
|
await ocean.metadataCache.waitForAqua(ddoWithBadUrl.id)
|
||||||
@ -350,6 +423,7 @@ describe('Marketplace flow', () => {
|
|||||||
await ocean.metadataCache.waitForAqua(ddoWithCredentialsAllowList.id)
|
await ocean.metadataCache.waitForAqua(ddoWithCredentialsAllowList.id)
|
||||||
await ocean.metadataCache.waitForAqua(ddoWithCredentialsDenyList.id)
|
await ocean.metadataCache.waitForAqua(ddoWithCredentialsDenyList.id)
|
||||||
await ocean.metadataCache.waitForAqua(ddoWithCredentials.id)
|
await ocean.metadataCache.waitForAqua(ddoWithCredentials.id)
|
||||||
|
await ocean.metadataCache.waitForAqua(ddoWithUserData.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Alice should fail to publish invalid dataset', async () => {
|
it('Alice should fail to publish invalid dataset', async () => {
|
||||||
@ -404,6 +478,7 @@ describe('Marketplace flow', () => {
|
|||||||
await datatoken.mint(tokenAddressForBadUrlAsset, alice.getId(), tokenAmount)
|
await datatoken.mint(tokenAddressForBadUrlAsset, alice.getId(), tokenAmount)
|
||||||
await datatoken.mint(tokenAddressEncrypted, alice.getId(), tokenAmount)
|
await datatoken.mint(tokenAddressEncrypted, alice.getId(), tokenAmount)
|
||||||
await datatoken.mint(tokenAddressWithPool, alice.getId(), tokenAmount)
|
await datatoken.mint(tokenAddressWithPool, alice.getId(), tokenAmount)
|
||||||
|
await datatoken.mint(tokenAddressWithUserData, alice.getId(), tokenAmount)
|
||||||
// since we are in barge, we can do this
|
// since we are in barge, we can do this
|
||||||
await datatoken.mint(ocean.pool.oceanAddress, owner.getId(), tokenAmount)
|
await datatoken.mint(ocean.pool.oceanAddress, owner.getId(), tokenAmount)
|
||||||
await datatoken.transfer(ocean.pool.oceanAddress, alice.getId(), '200', owner.getId())
|
await datatoken.transfer(ocean.pool.oceanAddress, alice.getId(), '200', owner.getId())
|
||||||
@ -447,18 +522,28 @@ describe('Marketplace flow', () => {
|
|||||||
|
|
||||||
it('Bob gets datatokens', async () => {
|
it('Bob gets datatokens', async () => {
|
||||||
const dTamount = '20'
|
const dTamount = '20'
|
||||||
await datatoken
|
let balance
|
||||||
.transfer(tokenAddress, bob.getId(), dTamount, alice.getId())
|
await datatoken.transfer(tokenAddress, bob.getId(), dTamount, alice.getId())
|
||||||
.then(async () => {
|
balance = await datatoken.balance(tokenAddress, bob.getId())
|
||||||
const balance = await datatoken.balance(tokenAddress, bob.getId())
|
assert(balance.toString() === dTamount.toString())
|
||||||
assert(balance.toString() === dTamount.toString())
|
|
||||||
})
|
await datatoken.transfer(
|
||||||
await datatoken
|
tokenAddressForBadUrlAsset,
|
||||||
.transfer(tokenAddressForBadUrlAsset, bob.getId(), dTamount, alice.getId())
|
bob.getId(),
|
||||||
.then(async () => {
|
dTamount,
|
||||||
const balance = await datatoken.balance(tokenAddressForBadUrlAsset, bob.getId())
|
alice.getId()
|
||||||
assert(balance.toString() === dTamount.toString())
|
)
|
||||||
})
|
balance = await datatoken.balance(tokenAddressForBadUrlAsset, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
|
|
||||||
|
await datatoken.transfer(
|
||||||
|
tokenAddressWithUserData,
|
||||||
|
bob.getId(),
|
||||||
|
dTamount,
|
||||||
|
alice.getId()
|
||||||
|
)
|
||||||
|
balance = await datatoken.balance(tokenAddressWithUserData, bob.getId())
|
||||||
|
assert(balance.toString() === dTamount.toString())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Bob consumes asset 1', async () => {
|
it('Bob consumes asset 1', async () => {
|
||||||
@ -772,4 +857,41 @@ describe('Marketplace flow', () => {
|
|||||||
assert(consumable.status === 3)
|
assert(consumable.status === 3)
|
||||||
assert(consumable.result === false)
|
assert(consumable.result === false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Bob tries to order asset with Custom Data, but he does not provide all the params', async () => {
|
||||||
|
try {
|
||||||
|
const order = await ocean.assets.order(
|
||||||
|
ddoWithUserData.id,
|
||||||
|
accessService.type,
|
||||||
|
bob.getId()
|
||||||
|
)
|
||||||
|
assert(order === null, 'Order should be null')
|
||||||
|
} catch (error) {
|
||||||
|
assert(error != null, 'Order should throw error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Bob tries to order asset with Custom Data, providing all required user inputs', async () => {
|
||||||
|
const bobUserData = {
|
||||||
|
firstname: 'Bob',
|
||||||
|
lastname: 'Doe'
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const service = ddoWithUserData.findServiceByType('access')
|
||||||
|
const serviceIndex = service.index
|
||||||
|
const order = await ocean.assets.order(
|
||||||
|
ddoWithUserData.id,
|
||||||
|
accessService.type,
|
||||||
|
bob.getId(),
|
||||||
|
serviceIndex,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
bobUserData
|
||||||
|
)
|
||||||
|
assert(order != null, 'Order should not be null')
|
||||||
|
} catch (error) {
|
||||||
|
assert(error === null, 'Order should not throw error')
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user