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
|
||||
|
||||
- run: npm run test:integration:cover
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: coverage
|
||||
|
11
.github/workflows/publish.yml
vendored
11
.github/workflows/publish.yml
vendored
@ -8,6 +8,8 @@ on:
|
||||
jobs:
|
||||
npm:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
@ -15,6 +17,11 @@ jobs:
|
||||
node-version: '16'
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- 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
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
if: ${{ !contains(github.ref, 'next') }}
|
||||
|
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)
|
||||
- [⬆️ Releases](#️-releases)
|
||||
- [Production](#production)
|
||||
- [Pre-releases](#pre-releases)
|
||||
- [🏛 License](#-license)
|
||||
|
||||
## 📚 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)
|
||||
|
||||
### 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
|
||||
|
||||
```
|
||||
|
@ -1,9 +1,23 @@
|
||||
import { Metadata } from './Metadata'
|
||||
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 interface ServiceCommonAttributes {
|
||||
export interface ServiceCommonAttributes extends ServiceCustomParametersRequired {
|
||||
main: { [key: string]: any }
|
||||
additionalInformation?: { [key: string]: any }
|
||||
status?: Status
|
||||
@ -33,9 +47,9 @@ export interface publisherTrustedAlgorithm {
|
||||
}
|
||||
|
||||
export interface ServiceComputePrivacy {
|
||||
allowRawAlgorithm: boolean
|
||||
allowNetworkAccess: boolean
|
||||
allowAllPublishedAlgorithms: boolean
|
||||
allowRawAlgorithm?: boolean
|
||||
allowNetworkAccess?: boolean
|
||||
allowAllPublishedAlgorithms?: boolean
|
||||
publisherTrustedAlgorithms?: publisherTrustedAlgorithm[]
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { DDO } from '../ddo/DDO'
|
||||
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 { EditableMetadata } from '../ddo/interfaces/EditableMetadata'
|
||||
import Account from './Account'
|
||||
@ -9,15 +14,11 @@ import { SubscribablePromise, didNoZeroX, didPrefixed, assetResolve } from '../u
|
||||
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
||||
import { WebServiceConnector } from './utils/WebServiceConnector'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { Provider } from '../provider/Provider'
|
||||
import { Provider, UserCustomParameters } from '../provider/Provider'
|
||||
import { isAddress } from 'web3-utils'
|
||||
import { MetadataMain } from '../ddo/interfaces'
|
||||
import { TransactionReceipt } from 'web3-core'
|
||||
import {
|
||||
CredentialType,
|
||||
CredentialAction,
|
||||
Credentials
|
||||
} from '../ddo/interfaces/Credentials'
|
||||
import { CredentialType } from '../ddo/interfaces/Credentials'
|
||||
import { updateCredentialDetail, removeCredentialDetail } from './AssetsCredential'
|
||||
import { Consumable } from '../ddo/interfaces/Consumable'
|
||||
|
||||
@ -71,7 +72,7 @@ export class Assets extends Instantiable {
|
||||
* @param {String} name Token name
|
||||
* @param {String} symbol Token symbol
|
||||
* @param {String} providerUri
|
||||
* @return {Promise<DDO>}
|
||||
* @return {SubscribablePromise<CreateProgressStep, DDO>}
|
||||
*/
|
||||
public create(
|
||||
metadata: Metadata,
|
||||
@ -443,17 +444,20 @@ export class Assets extends Instantiable {
|
||||
* @param {String} cost number of datatokens needed for this service
|
||||
* @param {String} datePublished
|
||||
* @param {Number} timeout
|
||||
* @return {Promise<string>} service
|
||||
* @param {String} providerUri
|
||||
* @param {ServiceCustomParametersRequired} requiredParameters
|
||||
* @return {Promise<ServiceAccess>} service
|
||||
*/
|
||||
|
||||
public async createAccessServiceAttributes(
|
||||
creator: Account,
|
||||
cost: string,
|
||||
datePublished: string,
|
||||
timeout = 0,
|
||||
providerUri?: string
|
||||
timeout: number = 0,
|
||||
providerUri?: string,
|
||||
requiredParameters?: ServiceCustomParametersRequired
|
||||
): Promise<ServiceAccess> {
|
||||
return {
|
||||
const service: ServiceAccess = {
|
||||
type: 'access',
|
||||
index: 2,
|
||||
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 {Number} serviceIndex
|
||||
* @param {String} serviceEndpoint
|
||||
* @param {UserCustomParameters} userCustomParameters
|
||||
* @return {Promise<any>} Order details
|
||||
*/
|
||||
public async initialize(
|
||||
asset: DDO | string,
|
||||
serviceType: string,
|
||||
consumerAddress: string,
|
||||
serviceIndex = -1,
|
||||
serviceEndpoint: string
|
||||
serviceIndex: number = -1,
|
||||
serviceEndpoint: string,
|
||||
userCustomParameters?: UserCustomParameters
|
||||
): Promise<any> {
|
||||
const provider = await Provider.getInstance(this.instanceConfig)
|
||||
await provider.setBaseUrl(serviceEndpoint)
|
||||
@ -492,7 +503,8 @@ export class Assets extends Instantiable {
|
||||
asset,
|
||||
serviceIndex,
|
||||
serviceType,
|
||||
consumerAddress
|
||||
consumerAddress,
|
||||
userCustomParameters
|
||||
)
|
||||
if (res === null) return null
|
||||
const providerData = JSON.parse(res)
|
||||
@ -507,15 +519,17 @@ export class Assets extends Instantiable {
|
||||
* @param {Number} serviceIndex
|
||||
* @param {String} mpAddress Marketplace fee collector address
|
||||
* @param {String} consumerAddress Optionally, if the consumer is another address than payer
|
||||
* @param {UserCustomParameters} userCustomParameters
|
||||
* @return {Promise<String>} transactionHash of the payment
|
||||
*/
|
||||
public async order(
|
||||
asset: DDO | string,
|
||||
serviceType: string,
|
||||
payerAddress: string,
|
||||
serviceIndex = -1,
|
||||
serviceIndex: number = -1,
|
||||
mpAddress?: string,
|
||||
consumerAddress?: string,
|
||||
userCustomParameters?: UserCustomParameters,
|
||||
searchPreviousOrders = true
|
||||
): Promise<string> {
|
||||
let service: Service
|
||||
@ -533,13 +547,25 @@ export class Assets extends Instantiable {
|
||||
service = await this.getServiceByIndex(ddo, serviceIndex)
|
||||
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 {
|
||||
const providerData = await this.initialize(
|
||||
ddo,
|
||||
serviceType,
|
||||
payerAddress,
|
||||
serviceIndex,
|
||||
service.serviceEndpoint
|
||||
service.serviceEndpoint,
|
||||
userCustomParameters
|
||||
)
|
||||
if (!providerData)
|
||||
throw new Error(
|
||||
@ -732,4 +758,25 @@ export class Assets extends Instantiable {
|
||||
*/
|
||||
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,
|
||||
ServiceComputePrivacy,
|
||||
ServiceCompute,
|
||||
publisherTrustedAlgorithm
|
||||
publisherTrustedAlgorithm,
|
||||
ServiceCustomParametersRequired
|
||||
} from '../ddo/interfaces/Service'
|
||||
import Account from './Account'
|
||||
import { SubscribablePromise, assetResolve, AssetResolved } from '../utils'
|
||||
@ -14,7 +15,7 @@ import {
|
||||
ComputeInput,
|
||||
ComputeAlgorithm
|
||||
} from './interfaces/Compute'
|
||||
import { Provider } from '../provider/Provider'
|
||||
import { Provider, UserCustomParameters } from '../provider/Provider'
|
||||
import { SHA256 } from 'crypto-js'
|
||||
|
||||
export enum OrderProgressStep {
|
||||
@ -121,6 +122,18 @@ export class Compute extends Instantiable {
|
||||
const { did, ddo } = await assetResolve(asset, this.ocean)
|
||||
const service = ddo.findServiceByType('compute')
|
||||
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) {
|
||||
const provider = await Provider.getInstance(this.instanceConfig)
|
||||
await provider.setBaseUrl(serviceEndpoint)
|
||||
@ -343,11 +356,12 @@ export class Compute extends Instantiable {
|
||||
providerAttributes: any,
|
||||
computePrivacy?: ServiceComputePrivacy,
|
||||
timeout?: number,
|
||||
providerUri?: string
|
||||
providerUri?: string,
|
||||
requiredCustomParameters?: ServiceCustomParametersRequired
|
||||
): ServiceCompute {
|
||||
const name = 'dataAssetComputingService'
|
||||
if (!timeout) timeout = 3600
|
||||
const service = {
|
||||
const service: ServiceCompute = {
|
||||
type: 'compute',
|
||||
index: 3,
|
||||
serviceEndpoint: providerUri || this.ocean.provider.url,
|
||||
@ -365,6 +379,12 @@ export class Compute extends Instantiable {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -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} 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.
|
||||
* @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,
|
||||
* 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,
|
||||
mpAddress?: string,
|
||||
computeAddress?: string,
|
||||
userCustomParameters?: UserCustomParameters,
|
||||
searchPreviousOrders = true
|
||||
): SubscribablePromise<OrderProgressStep, string> {
|
||||
return new SubscribablePromise(async (observer) => {
|
||||
@ -543,6 +564,7 @@ export class Compute extends Instantiable {
|
||||
-1,
|
||||
mpAddress,
|
||||
computeAddress,
|
||||
userCustomParameters,
|
||||
searchPreviousOrders
|
||||
)
|
||||
return order
|
||||
@ -570,6 +592,7 @@ export class Compute extends Instantiable {
|
||||
serviceIndex = -1,
|
||||
mpAddress?: string,
|
||||
consumerAddress?: string,
|
||||
userCustomParameters?: UserCustomParameters,
|
||||
searchPreviousOrders = true
|
||||
): Promise<string> {
|
||||
// this is only a convienince function, which calls ocean.assets.order
|
||||
@ -581,6 +604,7 @@ export class Compute extends Instantiable {
|
||||
serviceIndex,
|
||||
mpAddress,
|
||||
consumerAddress,
|
||||
userCustomParameters,
|
||||
searchPreviousOrders
|
||||
)
|
||||
} catch (error) {
|
||||
|
@ -43,4 +43,5 @@ export interface ComputeAlgorithm {
|
||||
meta?: MetadataAlgorithm
|
||||
transferTxId?: string
|
||||
dataToken?: string
|
||||
algoCustomParameters?: { [key: string]: any }
|
||||
}
|
||||
|
@ -18,6 +18,10 @@ export interface ServiceEndpoint {
|
||||
urlPath: string
|
||||
}
|
||||
|
||||
export interface UserCustomParameters {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an interface for provider service.
|
||||
* Provider service is the technical component executed
|
||||
@ -188,7 +192,8 @@ export class Provider extends Instantiable {
|
||||
asset: DDO | string,
|
||||
serviceIndex: number,
|
||||
serviceType: string,
|
||||
consumerAddress: string
|
||||
consumerAddress: string,
|
||||
userCustomParameters?: UserCustomParameters
|
||||
): Promise<string> {
|
||||
const { did, ddo } = await assetResolve(asset, this.ocean)
|
||||
let initializeUrl = this.getInitializeEndpoint()
|
||||
@ -200,6 +205,8 @@ export class Provider extends Instantiable {
|
||||
initializeUrl += `&serviceType=${serviceType}`
|
||||
initializeUrl += `&dataToken=${ddo.dataToken}`
|
||||
initializeUrl += `&consumerAddress=${consumerAddress}`
|
||||
if (userCustomParameters)
|
||||
initializeUrl += '&userdata=' + encodeURI(JSON.stringify(userCustomParameters))
|
||||
try {
|
||||
const response = await this.ocean.utils.fetch.get(initializeUrl)
|
||||
return await response.text()
|
||||
@ -218,7 +225,8 @@ export class Provider extends Instantiable {
|
||||
destination: string,
|
||||
account: Account,
|
||||
files: File[],
|
||||
index = -1
|
||||
index = -1,
|
||||
userCustomParameters?: UserCustomParameters
|
||||
): Promise<any> {
|
||||
await this.getNonce(account.getId())
|
||||
const signature = await this.createSignature(account, did + this.nonce)
|
||||
@ -236,7 +244,8 @@ export class Provider extends Instantiable {
|
||||
consumeUrl += `&transferTxId=${txId}`
|
||||
consumeUrl += `&consumerAddress=${account.getId()}`
|
||||
consumeUrl += `&signature=${signature}`
|
||||
|
||||
if (userCustomParameters)
|
||||
consumeUrl += '&userdata=' + encodeURI(JSON.stringify(userCustomParameters))
|
||||
try {
|
||||
!destination
|
||||
? await this.ocean.utils.fetch.downloadFileBrowser(consumeUrl)
|
||||
@ -262,7 +271,8 @@ export class Provider extends Instantiable {
|
||||
serviceIndex?: string,
|
||||
serviceType?: string,
|
||||
tokenAddress?: string,
|
||||
additionalInputs?: ComputeInput[]
|
||||
additionalInputs?: ComputeInput[],
|
||||
userCustomParameters?: UserCustomParameters
|
||||
): Promise<ComputeJob | ComputeJob[]> {
|
||||
const address = consumerAccount.getId()
|
||||
await this.getNonce(consumerAccount.getId())
|
||||
@ -291,6 +301,9 @@ export class Provider extends Instantiable {
|
||||
if (tokenAddress) payload.dataToken = tokenAddress
|
||||
|
||||
if (additionalInputs) payload.additionalInputs = additionalInputs
|
||||
if (userCustomParameters) payload.userData = userCustomParameters
|
||||
if (algorithm.algoCustomParameters)
|
||||
payload.algouserdata = algorithm.algoCustomParameters
|
||||
const path = this.getComputeStartEndpoint()
|
||||
? this.getComputeStartEndpoint().urlPath
|
||||
: null
|
||||
|
@ -42,6 +42,7 @@ describe('Compute flow', () => {
|
||||
let algorithmAssetwithCompute: DDO
|
||||
let algorithmAssetRemoteProvider: DDO
|
||||
let algorithmAssetRemoteProviderWithCompute: DDO
|
||||
let algorithmAssetWithCustomData: DDO
|
||||
let contracts: TestContractHandler
|
||||
let datatoken: DataTokens
|
||||
let tokenAddress: string
|
||||
@ -54,6 +55,7 @@ describe('Compute flow', () => {
|
||||
let tokenAddressAlgorithmRemoteProviderWithCompute: string
|
||||
let tokenAddressAdditional1: string
|
||||
let tokenAddressAdditional2: string
|
||||
let tokenAddressWithCustomData: string
|
||||
let price: string
|
||||
let ocean: Ocean
|
||||
let data: { t: number; url: string }
|
||||
@ -209,6 +211,18 @@ describe('Compute flow', () => {
|
||||
'Add2'
|
||||
)
|
||||
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 () => {
|
||||
@ -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 () => {
|
||||
await datatoken.mint(tokenAddress, 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(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 () => {
|
||||
let balance
|
||||
const dTamount = '200'
|
||||
await datatoken
|
||||
.transfer(tokenAddress, bob.getId(), dTamount, alice.getId())
|
||||
.then(async () => {
|
||||
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.transfer(tokenAddress, bob.getId(), dTamount, alice.getId())
|
||||
balance = await datatoken.balance(tokenAddress, bob.getId())
|
||||
assert(balance.toString() === dTamount.toString())
|
||||
|
||||
await datatoken
|
||||
.transfer(tokenAddressWithBogusProvider, bob.getId(), dTamount, alice.getId())
|
||||
.then(async () => {
|
||||
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.transfer(tokenAddressNoRawAlgo, bob.getId(), dTamount, alice.getId())
|
||||
balance = await datatoken.balance(tokenAddressNoRawAlgo, bob.getId())
|
||||
assert(balance.toString() === dTamount.toString())
|
||||
|
||||
await datatoken
|
||||
.transfer(tokenAddressAdditional1, bob.getId(), dTamount, alice.getId())
|
||||
.then(async () => {
|
||||
const balance = await datatoken.balance(tokenAddressAdditional1, bob.getId())
|
||||
assert(balance.toString() === dTamount.toString())
|
||||
})
|
||||
await datatoken.transfer(
|
||||
tokenAddressWithTrustedAlgo,
|
||||
bob.getId(),
|
||||
dTamount,
|
||||
alice.getId()
|
||||
)
|
||||
balance = await datatoken.balance(tokenAddressWithTrustedAlgo, bob.getId())
|
||||
assert(balance.toString() === dTamount.toString())
|
||||
|
||||
await datatoken
|
||||
.transfer(tokenAddressAdditional2, bob.getId(), dTamount, alice.getId())
|
||||
.then(async () => {
|
||||
const balance = await datatoken.balance(tokenAddressAdditional2, bob.getId())
|
||||
assert(balance.toString() === dTamount.toString())
|
||||
})
|
||||
await datatoken.transfer(
|
||||
tokenAddressWithBogusProvider,
|
||||
bob.getId(),
|
||||
dTamount,
|
||||
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 () => {
|
||||
@ -1370,7 +1481,176 @@ describe('Compute flow', () => {
|
||||
assert(response.status >= 1, 'Invalid response.status')
|
||||
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 () => {
|
||||
const computeService = ddo.findServiceByType('compute')
|
||||
assert(computeService, 'ComputeIndex should be >0')
|
||||
|
@ -35,11 +35,13 @@ describe('Marketplace flow', () => {
|
||||
let ddoWithCredentialsAllowList
|
||||
let ddoWithCredentialsDenyList
|
||||
let ddoWithCredentials
|
||||
let ddoWithUserData
|
||||
let asset
|
||||
let assetWithPool
|
||||
let assetWithBadUrl
|
||||
let assetWithEncrypt
|
||||
let assetInvalidNoName
|
||||
let assetWithUserData
|
||||
let marketplace: Account
|
||||
let contracts: TestContractHandler
|
||||
let datatoken: DataTokens
|
||||
@ -48,6 +50,7 @@ describe('Marketplace flow', () => {
|
||||
let tokenAddressForBadUrlAsset: string
|
||||
let tokenAddressEncrypted: string
|
||||
let tokenAddressInvalidNoName: string
|
||||
let tokenAddressWithUserData
|
||||
let service1: ServiceAccess
|
||||
let price: string
|
||||
let ocean: Ocean
|
||||
@ -133,6 +136,15 @@ describe('Marketplace flow', () => {
|
||||
'DTA'
|
||||
)
|
||||
assert(tokenAddressInvalidNoName != null)
|
||||
|
||||
tokenAddressWithUserData = await datatoken.create(
|
||||
blob,
|
||||
alice.getId(),
|
||||
'10000000000',
|
||||
'AliceDT',
|
||||
'DTA'
|
||||
)
|
||||
assert(tokenAddressWithUserData != null)
|
||||
})
|
||||
|
||||
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 () => {
|
||||
const valid = await ocean.metadataCache.validateMetadata(asset)
|
||||
@ -342,6 +375,46 @@ describe('Marketplace flow', () => {
|
||||
false
|
||||
)
|
||||
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
|
||||
await ocean.metadataCache.waitForAqua(ddo.id)
|
||||
await ocean.metadataCache.waitForAqua(ddoWithBadUrl.id)
|
||||
@ -350,6 +423,7 @@ describe('Marketplace flow', () => {
|
||||
await ocean.metadataCache.waitForAqua(ddoWithCredentialsAllowList.id)
|
||||
await ocean.metadataCache.waitForAqua(ddoWithCredentialsDenyList.id)
|
||||
await ocean.metadataCache.waitForAqua(ddoWithCredentials.id)
|
||||
await ocean.metadataCache.waitForAqua(ddoWithUserData.id)
|
||||
})
|
||||
|
||||
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(tokenAddressEncrypted, 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
|
||||
await datatoken.mint(ocean.pool.oceanAddress, owner.getId(), tokenAmount)
|
||||
await datatoken.transfer(ocean.pool.oceanAddress, alice.getId(), '200', owner.getId())
|
||||
@ -447,18 +522,28 @@ describe('Marketplace flow', () => {
|
||||
|
||||
it('Bob gets datatokens', async () => {
|
||||
const dTamount = '20'
|
||||
await datatoken
|
||||
.transfer(tokenAddress, bob.getId(), dTamount, alice.getId())
|
||||
.then(async () => {
|
||||
const balance = await datatoken.balance(tokenAddress, bob.getId())
|
||||
assert(balance.toString() === dTamount.toString())
|
||||
})
|
||||
await datatoken
|
||||
.transfer(tokenAddressForBadUrlAsset, bob.getId(), dTamount, alice.getId())
|
||||
.then(async () => {
|
||||
const balance = await datatoken.balance(tokenAddressForBadUrlAsset, bob.getId())
|
||||
assert(balance.toString() === dTamount.toString())
|
||||
})
|
||||
let balance
|
||||
await datatoken.transfer(tokenAddress, bob.getId(), dTamount, alice.getId())
|
||||
balance = await datatoken.balance(tokenAddress, bob.getId())
|
||||
assert(balance.toString() === dTamount.toString())
|
||||
|
||||
await datatoken.transfer(
|
||||
tokenAddressForBadUrlAsset,
|
||||
bob.getId(),
|
||||
dTamount,
|
||||
alice.getId()
|
||||
)
|
||||
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 () => {
|
||||
@ -772,4 +857,41 @@ describe('Marketplace flow', () => {
|
||||
assert(consumable.status === 3)
|
||||
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