1
0
mirror of https://github.com/oceanprotocol-archive/squid-js.git synced 2024-02-02 15:31:51 +01:00

new ocean.utils.services

* refactor all .order() methods to use one single method
* remove `index` parameter from ocean.assets.order()
* update compute test flow
This commit is contained in:
Matthias Kretschmann 2020-01-27 19:14:12 +01:00
parent ac39369543
commit 40754ca46a
Signed by: m
GPG Key ID: 606EEEF3C479A91F
13 changed files with 336 additions and 428 deletions

View File

@ -90,9 +90,7 @@ describe('Asset Owners', () => {
)
} catch {}
const { index } = ddo.findServiceByType('access')
await ocean.assets.order(ddo.id, index, account2)
await ocean.assets.order(ddo.id, account2)
// Access granted
const { length: finalLength2 } = await ocean.assets.consumerAssets(

View File

@ -18,13 +18,45 @@ describe('Compute', () => {
before(async () => {
ocean = await Ocean.getInstance(config)
;[account] = await ocean.accounts.list()
ddoAsset = await ocean.assets.create(metadataAsset as MetaData, account, [
computeServiceExample
])
ddoAlgorithm = await ocean.assets.create(metadataAlgorithm as MetaData, account)
})
// order compute service
agreementId = await ocean.compute.order(account, ddoAsset.id)
it('should authenticate the consumer account', async () => {
await account.authenticate()
})
it('should publish a dataset and an algorithm', async () => {
const stepsAsset = []
ddoAsset = await ocean.assets
.create(metadataAsset as MetaData, account, [computeServiceExample])
.next(step => stepsAsset.push(step))
assert.instanceOf(ddoAsset, DDO)
assert.deepEqual(stepsAsset, [0, 1, 2, 3, 4, 5, 6, 7])
const stepsAlgorithm = []
ddoAlgorithm = await ocean.assets
.create(metadataAlgorithm as MetaData, account)
.next(step => stepsAlgorithm.push(step))
assert.instanceOf(ddoAlgorithm, DDO)
assert.deepEqual(stepsAlgorithm, [0, 1, 2, 3, 4, 5, 6, 7])
})
it('should order the compute service', async () => {
const steps = []
try {
await account.requestTokens(
+computeServiceExample.attributes.main.price *
10 ** -(await ocean.keeper.token.decimals())
)
agreementId = await ocean.compute
.order(account, ddoAsset.id)
.next(step => steps.push(step))
} catch {}
assert.isDefined(agreementId)
assert.deepEqual(steps, [0, 1, 2, 3])
})
it('should start a compute job', async () => {

View File

@ -50,18 +50,17 @@ describe('Consume Asset (Brizo)', () => {
})
it('should order the asset', async () => {
const accessService = ddo.findServiceByType('access')
const steps = []
try {
await consumer.requestTokens(
+metadata.main.price * 10 ** -(await ocean.keeper.token.decimals())
)
} catch {}
const steps = []
agreementId = await ocean.assets
.order(ddo.id, accessService.index, consumer)
.next(step => steps.push(step))
agreementId = await ocean.assets
.order(ddo.id, consumer)
.next(step => steps.push(step))
} catch {}
assert.isDefined(agreementId)
assert.deepEqual(steps, [0, 1, 2, 3])

View File

@ -50,15 +50,13 @@ xdescribe('Consume Asset (Large size)', () => {
})
it('should order the asset', async () => {
const accessService = ddo.findServiceByType('access')
try {
await consumer.requestTokens(
+metadata.main.price * 10 ** -(await ocean.keeper.token.decimals())
)
} catch {}
agreementId = await ocean.assets.order(ddo.id, accessService.index, consumer)
agreementId = await ocean.assets.order(ddo.id, consumer)
assert.isDefined(agreementId)
})

View File

@ -1,6 +1,6 @@
import { MetaData } from '../../src' // @oceanprotocol/squid
import { ServiceType } from '../../src/ddo/Service'
import ddoCompute from '../../test/testdata/ddo-compute.json'
import ddoExample from '../../test/testdata/ddo.json'
const metadata: Partial<MetaData> = {
main: {
@ -83,7 +83,7 @@ export const getMetadata = (price?: number, type?: 'dataset' | 'algorithm') =>
generateMetadata('TestAsset', type, price)
export const getComputeServiceExample = () => {
const computeService = ddoCompute.service.find(service => service.type === 'compute')
const computeService = ddoExample.service.find(service => service.type === 'compute')
const { index, serviceEndpoint, templateId, attributes } = computeService
return {

View File

@ -34,10 +34,10 @@ export interface ServiceAccess extends ServiceCommon {
price: string
timeout: number
}
serviceAgreementTemplate?: ServiceAgreementTemplate
additionalInformation: {
description: string
}
serviceAgreementTemplate?: ServiceAgreementTemplate
}
}
@ -51,8 +51,8 @@ export interface ServiceCompute extends ServiceCommon {
price: string
timeout: number
provider?: ServiceComputeProvider
serviceAgreementTemplate?: ServiceAgreementTemplate
}
serviceAgreementTemplate?: ServiceAgreementTemplate
}
}

View File

@ -5,8 +5,9 @@ import { MetaData } from '../ddo/MetaData'
import { Service } from '../ddo/Service'
import Account from './Account'
import DID from './DID'
import { fillConditionsWithDDO, SubscribablePromise, generateId, zeroX } from '../utils'
import { fillConditionsWithDDO, SubscribablePromise } from '../utils'
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
import { OrderProgressStep } from './utils/ServiceUtils'
export enum CreateProgressStep {
EncryptingFiles,
@ -19,13 +20,6 @@ export enum CreateProgressStep {
DdoStored
}
export enum OrderProgressStep {
CreatingAgreement,
AgreementInitialized,
LockingPayment,
LockedPayment
}
/**
* Assets submodule of Ocean Protocol.
*/
@ -284,95 +278,28 @@ export class OceanAssets extends Instantiable {
* Start the purchase/order of an asset's service. Starts by signing the service agreement
* then sends the request to the publisher via the service endpoint (Brizo http service).
* @param {string} did Decentralized ID.
* @param {number} index Service index.
* @param {Account} consumerAccount Consumer account.
* @param {string} provider ethereum address of service provider (optional)
* @return {Promise<string>} Returns Agreement ID
*/
public order(
did: string,
index: number,
consumerAccount: Account,
provider?: string
): SubscribablePromise<OrderProgressStep, string> {
return new SubscribablePromise(async observer => {
const oceanAgreements = this.ocean.agreements
const { keeper, utils } = this.ocean
const ddo: DDO = await this.resolve(did)
const condition = keeper.conditions.accessSecretStoreCondition
const agreementId = zeroX(generateId())
const ddo = await this.resolve(did)
const { keeper } = this.ocean
const templateName = ddo.findServiceByType('access').attributes
.serviceAgreementTemplate.contractName
const template = keeper.getTemplateByName(templateName)
const accessCondition = keeper.conditions.accessSecretStoreCondition
// eslint-disable-next-line no-async-promise-executor
const paymentFlow = new Promise(async (resolve, reject) => {
await template.getAgreementCreatedEvent(agreementId).once()
this.logger.log('Agreement initialized')
observer.next(OrderProgressStep.AgreementInitialized)
const { attributes } = ddo.findServiceByType('metadata')
this.logger.log('Locking payment')
const accessGranted = accessCondition
.getConditionFulfilledEvent(agreementId)
.once()
observer.next(OrderProgressStep.LockingPayment)
const paid = await oceanAgreements.conditions.lockReward(
agreementId,
attributes.main.price,
consumerAccount
)
observer.next(OrderProgressStep.LockedPayment)
if (paid) {
this.logger.log('Payment was OK')
} else {
this.logger.error('Payment was KO')
this.logger.error('Agreement ID: ', agreementId)
this.logger.error('DID: ', ddo.id)
reject(new Error('Error on payment'))
}
await accessGranted
this.logger.log('Access granted')
resolve()
})
observer.next(OrderProgressStep.CreatingAgreement)
this.logger.log('Creating agreement')
// Get provider from didRegistry if not given in arguments
let _provider = provider
if (!provider) {
const providers = await keeper.didRegistry.getDIDProviders(ddo.shortId())
if (providers) {
_provider = providers[0]
}
}
await oceanAgreements.create(
did,
agreementId,
index,
undefined,
const agreementId = await utils.services.order(
'access',
condition,
observer,
consumerAccount,
_provider,
consumerAccount
ddo,
provider
)
this.logger.log('Agreement created')
try {
await paymentFlow
} catch (e) {
throw new Error('Error paying the asset.')
}
return agreementId
})

View File

@ -2,8 +2,8 @@ import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
import { MetaData } from '../ddo/MetaData'
import Account from './Account'
import { DDO } from '../ddo/DDO'
import { SubscribablePromise, generateId, zeroX } from '../utils'
import { OrderProgressStep } from './OceanAssets'
import { SubscribablePromise } from '../utils'
import { OrderProgressStep } from './utils/ServiceUtils'
export interface ComputeJobStatus {
owner: string
@ -48,80 +48,18 @@ export class OceanCompute extends Instantiable {
provider?: string
): SubscribablePromise<OrderProgressStep, string> {
return new SubscribablePromise(async observer => {
const { keeper, assets, agreements } = this.ocean
const agreementId = zeroX(generateId())
const { assets, keeper, utils } = this.ocean
const ddo: DDO = await assets.resolve(datasetDid)
const { index, attributes } = ddo.findServiceByType('compute')
const condition = keeper.conditions.computeExecutionCondition
const templateName = attributes.main.serviceAgreementTemplate.contractName
const template = keeper.getTemplateByName(templateName)
const computeCondition = keeper.conditions.computeExecutionCondition
// eslint-disable-next-line no-async-promise-executor
const paymentFlow = new Promise(async (resolve, reject) => {
await template.getAgreementCreatedEvent(agreementId).once()
this.logger.log('Agreement initialized')
observer.next(OrderProgressStep.AgreementInitialized)
this.logger.log('Locking payment')
const computeGranted = computeCondition
.getConditionFulfilledEvent(agreementId)
.once()
observer.next(OrderProgressStep.LockingPayment)
const paid = await agreements.conditions.lockReward(
agreementId,
attributes.main.price,
consumerAccount
)
observer.next(OrderProgressStep.LockedPayment)
if (paid) {
this.logger.log('Payment was OK')
} else {
this.logger.error('Payment was KO')
this.logger.error('Agreement ID: ', agreementId)
this.logger.error('DID: ', ddo.id)
reject(new Error('Error on payment'))
}
await computeGranted
this.logger.log('Compute granted')
resolve()
})
observer.next(OrderProgressStep.CreatingAgreement)
this.logger.log('Creating agreement')
// Get provider from didRegistry if not given in arguments
let _provider = provider
if (!provider) {
const providers = await keeper.didRegistry.getDIDProviders(ddo.shortId())
if (providers) {
_provider = providers[0]
}
}
await agreements.create(
datasetDid,
agreementId,
index,
undefined,
const agreementId = await utils.services.order(
'compute',
condition,
observer,
consumerAccount,
_provider,
consumerAccount
ddo,
provider
)
this.logger.log('Agreement created')
try {
await paymentFlow
} catch (e) {
throw new Error('Error paying the compute service.')
}
return agreementId
})

View File

@ -1,5 +1,6 @@
import { Instantiable, InstantiableConfig } from '../../Instantiable.abstract'
import { ServiceUtils } from './ServiceUtils'
import { ServiceAgreement } from './ServiceAgreement'
import { SignatureUtils } from './SignatureUtils'
import { WebServiceConnector } from './WebServiceConnector'
@ -21,6 +22,7 @@ export class OceanUtils extends Instantiable {
config.logger,
config.web3
)
instance.services = new ServiceUtils(config.ocean, config.logger)
instance.signature = new SignatureUtils(config.web3, config.logger)
instance.fetch = new WebServiceConnector(config.logger)
@ -33,6 +35,12 @@ export class OceanUtils extends Instantiable {
*/
public agreements: ServiceAgreement
/**
* Service utils.
* @type {ServiceUtils}
*/
public services: ServiceUtils
/**
* Signature utils.
* @type {SignatureUtils}

View File

@ -0,0 +1,113 @@
import { DDO } from '../../ddo/DDO'
import Account from '../Account'
import { zeroX, Logger, generateId } from '../../utils'
import { Ocean } from '../../squid'
import { Condition } from '../../keeper/contracts/conditions'
import { ServiceType } from '../../ddo/Service'
export enum OrderProgressStep {
CreatingAgreement,
AgreementInitialized,
LockingPayment,
LockedPayment
}
export class ServiceUtils {
private ocean: Ocean
private logger: Logger
constructor(ocean: Ocean, logger: Logger) {
this.ocean = ocean
this.logger = logger
}
public async order(
type: ServiceType,
condition: Condition,
observer: any,
consumerAccount: Account,
ddo: DDO,
provider?: string
): Promise<string> {
const { keeper, agreements } = this.ocean
const agreementId = zeroX(generateId())
const { index, attributes } = ddo.findServiceByType(type)
const metadata = ddo.findServiceByType('metadata')
const templateName = attributes.serviceAgreementTemplate.contractName
const template = keeper.getTemplateByName(templateName)
// use price from compute service,
// otherwise always take the price from metadata
const price =
type === 'compute' ? attributes.main.price : metadata.attributes.main.price
// eslint-disable-next-line no-async-promise-executor
const paymentFlow = new Promise(async (resolve, reject) => {
await template.getAgreementCreatedEvent(agreementId).once()
this.logger.log('Agreement initialized')
observer.next(OrderProgressStep.AgreementInitialized)
this.logger.log('Locking payment')
const serviceGranted = condition
.getConditionFulfilledEvent(agreementId)
.once()
observer.next(OrderProgressStep.LockingPayment)
const paid = await agreements.conditions.lockReward(
agreementId,
price,
consumerAccount
)
observer.next(OrderProgressStep.LockedPayment)
if (paid) {
this.logger.log('Payment was OK')
} else {
this.logger.error('Payment was KO')
this.logger.error('Agreement ID: ', agreementId)
this.logger.error('DID: ', ddo.id)
reject(new Error('Error on payment'))
}
await serviceGranted
this.logger.log(`Service ${type} granted`)
resolve()
})
observer.next(OrderProgressStep.CreatingAgreement)
this.logger.log('Creating agreement')
// Get provider from didRegistry if not given in arguments
let _provider = provider
if (!provider) {
const providers = await keeper.didRegistry.getDIDProviders(ddo.shortId())
if (providers) {
_provider = providers[0]
}
}
await agreements.create(
ddo.id,
agreementId,
index,
undefined,
consumerAccount,
_provider,
consumerAccount
)
this.logger.log('Agreement created')
try {
await paymentFlow
} catch (e) {
throw new Error(`Error paying the ${type} service.`)
}
return agreementId
}
}

View File

@ -14,7 +14,8 @@ import * as utils from './utils'
export * from './ddo/DDO'
export * from './ddo/MetaData'
export { OrderProgressStep, CreateProgressStep } from './ocean/OceanAssets'
export { CreateProgressStep } from './ocean/OceanAssets'
export { OrderProgressStep } from './ocean/utils/ServiceUtils'
export {
OceanPlatformTechStatus,
OceanPlatformTech,

View File

@ -1,248 +0,0 @@
{
"@context": "https://w3id.org/future-method/v1",
"authentication": [],
"created": "2019-04-09T19:02:11Z",
"id": "did:op:8d1b4d73e7af4634958f071ab8dfe7ab0df14019755e444090fd392c8ec9c3f4",
"proof": {
"created": "2019-04-09T19:02:11Z",
"creator": "0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e",
"signatureValue": "1cd57300733bcbcda0beb59b3e076de6419c0d7674e7befb77820b53c79e3aa8f1776effc64cf088bad8cb694cc4d71ebd74a13b2f75893df5a53f3f318f6cf828",
"type": "DDOIntegritySignature"
},
"publicKey": [
{
"id": "did:op:8d1b4d73e7af4634958f071ab8dfe7ab0df14019755e444090fd392c8ec9c3f4",
"owner": "0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e",
"type": "EthereumECDSAKey"
}
],
"service": [
{
"type": "metadata",
"index": 0,
"serviceEndpoint": "http://myaquarius.org/api/v1/provider/assets/metadata/{did}",
"attributes": {
"main": {
"author": "Met Office",
"dateCreated": "2019-02-08T08:13:49Z",
"files": [
{
"url": "https://raw.githubusercontent.com/tbertinmahieux/MSongsDB/master/Tasks_Demos/CoverSongs/shs_dataset_test.txt",
"index": 0,
"checksum": "efb2c764274b745f5fc37f97c6b0e764",
"contentLength": "4535431",
"contentType": "text/csv",
"encoding": "UTF-8",
"compression": "zip"
}
],
"license": "CC-BY",
"name": "UK Weather information 2011",
"price": "1",
"type": "dataset"
},
"additionalInformation": {}
}
},
{
"type": "compute",
"index": 1,
"serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/compute",
"templateId": "",
"attributes": {
"main": {
"name": "dataAssetComputingServiceAgreement",
"creator": "0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e",
"datePublished": "2019-04-09T19:02:11Z",
"price": "10",
"timeout": 86400,
"provider": {
"type": "Azure",
"description": "",
"environment": {
"cluster": {
"type": "Kubernetes",
"url": "http://10.0.0.17/xxx"
},
"supportedContainers": [
{
"image": "tensorflow/tensorflow",
"tag": "latest",
"checksum": "sha256:cb57ecfa6ebbefd8ffc7f75c0f00e57a7fa739578a429b6f72a0df19315deadc"
},
{
"image": "tensorflow/tensorflow",
"tag": "latest",
"checksum": "sha256:cb57ecfa6ebbefd8ffc7f75c0f00e57a7fa739578a429b6f72a0df19315deadc"
}
],
"supportedServers": [
{
"serverId": "1",
"serverType": "xlsize",
"price": "50",
"cpu": "16",
"gpu": "0",
"memory": "128gb",
"disk": "160gb",
"maxExecutionTime": 86400
},
{
"serverId": "2",
"serverType": "medium",
"price": "10",
"cpu": "2",
"gpu": "0",
"memory": "8gb",
"disk": "80gb",
"maxExecutionTime": 86400
}
]
}
}
},
"additionalInformation": {}
},
"serviceAgreementTemplate": {
"contractName": "EscrowComputeExecutionTemplate",
"events": [
{
"name": "AgreementCreated",
"actorType": "consumer",
"handler": {
"moduleName": "serviceExecutionTemplate",
"functionName": "fulfillLockRewardCondition",
"version": "0.1"
}
}
],
"fulfillmentOrder": [
"lockReward.fulfill",
"serviceExecution.fulfill",
"escrowReward.fulfill"
],
"conditionDependency": {
"lockReward": [],
"serviceExecution": [],
"releaseReward": ["lockReward", "serviceExecution"]
},
"conditions": [
{
"name": "lockReward",
"timelock": 0,
"timeout": 0,
"contractName": "LockRewardCondition",
"functionName": "fulfill",
"parameters": [
{
"name": "_rewardAddress",
"type": "address",
"value": ""
},
{
"name": "_amount",
"type": "uint256",
"value": ""
}
],
"events": [
{
"name": "Fulfilled",
"actorType": "publisher",
"handler": {
"moduleName": "lockRewardCondition",
"functionName": "fulfillServiceExecutionCondition",
"version": "0.1"
}
}
]
},
{
"name": "serviceExecution",
"timelock": 0,
"timeout": 0,
"contractName": "ComputeExecutionCondition",
"functionName": "fulfill",
"parameters": [
{
"name": "_documentId",
"type": "bytes32",
"value": ""
},
{
"name": "_grantee",
"type": "address",
"value": ""
}
],
"events": [
{
"name": "Fulfilled",
"actorType": "publisher",
"handler": {
"moduleName": "serviceExecution",
"functionName": "fulfillServiceExecutionCondition",
"version": "0.1"
}
},
{
"name": "TimedOut",
"actorType": "consumer",
"handler": {
"moduleName": "serviceExec",
"functionName": "fulfillServiceExecutionCondition",
"version": "0.1"
}
}
]
},
{
"name": "escrowReward",
"timelock": 0,
"timeout": 0,
"contractName": "EscrowReward",
"functionName": "fulfill",
"parameters": [
{
"name": "_amount",
"type": "uint256",
"value": ""
},
{
"name": "_receiver",
"type": "address",
"value": ""
},
{
"name": "_sender",
"type": "address",
"value": ""
},
{
"name": "_lockCondition",
"type": "bytes32",
"value": ""
},
{
"name": "_releaseCondition",
"type": "bytes32",
"value": ""
}
],
"events": [
{
"name": "Fulfilled",
"actorType": "publisher",
"handler": {
"moduleName": "escrowRewardCondition",
"functionName": "verifyRewardTokens",
"version": "0.1"
}
}
]
}
]
}
}
]
}

144
test/testdata/ddo.json vendored
View File

@ -147,9 +147,10 @@
"type": "compute",
"index": 1,
"serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/compute",
"templateId": "804932804923850985093485039850349850439583409583404534231321131a",
"templateId": "",
"attributes": {
"main": {
"name": "dataAssetComputingServiceAgreement",
"creator": "0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e",
"datePublished": "2019-04-09T19:02:11Z",
"price": "10",
@ -198,6 +199,147 @@
]
}
}
},
"additionalInformation": {},
"serviceAgreementTemplate": {
"contractName": "EscrowComputeExecutionTemplate",
"events": [
{
"name": "AgreementCreated",
"actorType": "consumer",
"handler": {
"moduleName": "serviceExecutionTemplate",
"functionName": "fulfillLockRewardCondition",
"version": "0.1"
}
}
],
"fulfillmentOrder": [
"lockReward.fulfill",
"serviceExecution.fulfill",
"escrowReward.fulfill"
],
"conditionDependency": {
"lockReward": [],
"serviceExecution": [],
"releaseReward": ["lockReward", "serviceExecution"]
},
"conditions": [
{
"name": "lockReward",
"timelock": 0,
"timeout": 0,
"contractName": "LockRewardCondition",
"functionName": "fulfill",
"parameters": [
{
"name": "_rewardAddress",
"type": "address",
"value": ""
},
{
"name": "_amount",
"type": "uint256",
"value": ""
}
],
"events": [
{
"name": "Fulfilled",
"actorType": "publisher",
"handler": {
"moduleName": "lockRewardCondition",
"functionName": "fulfillServiceExecutionCondition",
"version": "0.1"
}
}
]
},
{
"name": "serviceExecution",
"timelock": 0,
"timeout": 0,
"contractName": "ComputeExecutionCondition",
"functionName": "fulfill",
"parameters": [
{
"name": "_documentId",
"type": "bytes32",
"value": ""
},
{
"name": "_grantee",
"type": "address",
"value": ""
}
],
"events": [
{
"name": "Fulfilled",
"actorType": "publisher",
"handler": {
"moduleName": "serviceExecution",
"functionName": "fulfillServiceExecutionCondition",
"version": "0.1"
}
},
{
"name": "TimedOut",
"actorType": "consumer",
"handler": {
"moduleName": "serviceExec",
"functionName": "fulfillServiceExecutionCondition",
"version": "0.1"
}
}
]
},
{
"name": "escrowReward",
"timelock": 0,
"timeout": 0,
"contractName": "EscrowReward",
"functionName": "fulfill",
"parameters": [
{
"name": "_amount",
"type": "uint256",
"value": ""
},
{
"name": "_receiver",
"type": "address",
"value": ""
},
{
"name": "_sender",
"type": "address",
"value": ""
},
{
"name": "_lockCondition",
"type": "bytes32",
"value": ""
},
{
"name": "_releaseCondition",
"type": "bytes32",
"value": ""
}
],
"events": [
{
"name": "Fulfilled",
"actorType": "publisher",
"handler": {
"moduleName": "escrowRewardCondition",
"functionName": "verifyRewardTokens",
"version": "0.1"
}
}
]
}
]
}
}
},