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

Merge pull request #328 from oceanprotocol/v2

v2 release: new DDO
This commit is contained in:
Matthias Kretschmann 2019-12-09 12:33:03 +01:00 committed by GitHub
commit 90baab2f87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
93 changed files with 5728 additions and 3186 deletions

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v12

View File

@ -1,5 +1,6 @@
{ {
"semi": false, "semi": false,
"singleQuote": true, "singleQuote": true,
"printWidth": 90,
"trailingComma": "none" "trailingComma": "none"
} }

View File

@ -2,7 +2,7 @@ dist: xenial
sudo: required sudo: required
language: node_js language: node_js
node_js: node_js:
- '11' - '12'
services: services:
- docker - docker
@ -21,10 +21,10 @@ before_script:
- ganache-cli --port 18545 > ganache-cli.log & - ganache-cli --port 18545 > ganache-cli.log &
- git clone https://github.com/oceanprotocol/barge - git clone https://github.com/oceanprotocol/barge
- cd barge - cd barge
- export AQUARIUS_VERSION=v0.3.8 - export AQUARIUS_VERSION=v1.0.5
- export BRIZO_VERSION=v0.4.5 - export BRIZO_VERSION=v0.7.2
- export KEEPER_VERSION=v0.12.7 - export KEEPER_VERSION=v0.12.7
- export EVENTS_HANDLER_VERSION=v0.1.2 - export EVENTS_HANDLER_VERSION=v0.3.4
- export KEEPER_OWNER_ROLE_ADDRESS="0xe2DD09d719Da89e5a3D0F2549c7E24566e947260" - export KEEPER_OWNER_ROLE_ADDRESS="0xe2DD09d719Da89e5a3D0F2549c7E24566e947260"
- rm -rf "${HOME}/.ocean/keeper-contracts/artifacts" - rm -rf "${HOME}/.ocean/keeper-contracts/artifacts"
- bash -x start_ocean.sh --no-commons --no-dashboard 2>&1 > start_ocean.log & - bash -x start_ocean.sh --no-commons --no-dashboard 2>&1 > start_ocean.log &
@ -32,10 +32,10 @@ before_script:
script: script:
- npm run lint - npm run lint
- ./scripts/keeper.sh
- export ETH_PORT=18545; npm run test:cover - export ETH_PORT=18545; npm run test:cover
- npm run build - npm run build
- npm run doc - npm run doc
- ./scripts/keeper.sh
- npm run integration:cover - npm run integration:cover
- npm run report-coverage - npm run report-coverage

View File

@ -4,11 +4,20 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [v1.0.0-beta.7](https://github.com/oceanprotocol/squid-js/compare/v0.8.3...v1.0.0-beta.7)
> 22 November 2019
- fresh package-lock [`0552bcc`](https://github.com/oceanprotocol/squid-js/commit/0552bcc577fb83a5e9d683cb23dcbdfd23f0fa1c)
- Change linter config. [`3d7d1f3`](https://github.com/oceanprotocol/squid-js/commit/3d7d1f3972a665ccce4a1d712077435c1cd87459)
- tweak printWidth [`143fcbe`](https://github.com/oceanprotocol/squid-js/commit/143fcbecf3661d6a0da7f7d44f74d82caca41ad1)
#### [v0.8.3](https://github.com/oceanprotocol/squid-js/compare/v0.8.2...v0.8.3) #### [v0.8.3](https://github.com/oceanprotocol/squid-js/compare/v0.8.2...v0.8.3)
> 22 November 2019 > 22 November 2019
- added xDai [`#336`](https://github.com/oceanprotocol/squid-js/pull/336) - added xDai [`#336`](https://github.com/oceanprotocol/squid-js/pull/336)
- Release 0.8.3 [`c8e9bd2`](https://github.com/oceanprotocol/squid-js/commit/c8e9bd24c3850c250df02197582e430dee97152b)
#### [v0.8.2](https://github.com/oceanprotocol/squid-js/compare/v0.8.1...v0.8.2) #### [v0.8.2](https://github.com/oceanprotocol/squid-js/compare/v0.8.1...v0.8.2)

194
MIGRATION.md Normal file
View File

@ -0,0 +1,194 @@
# Migration Guide
Instructions on how to migrate between breaking versions.
## v0.8.3 → v1.0.0
### Ocean Protocol Components Requirements
squid-js v1.0.0 only works against:
- Aquarius v1.0.2+
- Brizo v0.7.0+
- Events Handler v0.3.0+
- Keeper Contracts v0.12.7+
### DDO Structure
The whole structure of the DDO has been changed based on [OEP-12](https://github.com/oceanprotocol/OEPs/tree/master/12) and [OEP-8 v0.4](https://github.com/oceanprotocol/OEPs/tree/master/8/v0.4) to better accomodate the DDOs to hold additional services, like execution of workflows and algorithms.
For migrating, you only have to deal with the `metadata` service holding the asset metadata, which was restructured too:
old:
```json
{
"service": [
{
"serviceDefinitionId": "0",
"serviceEndpoint": "http://localhost:5000/api/v1/aquarius/assets/ddo/{did}",
"type": "Metadata",
"metadata": {
"base": {
"name": "Madrid Weather forecast",
"description": "Wheater forecast of Europe/Madrid in XML format",
"dateCreated": "2019-05-16T12:36:14.535Z",
"author": "Norwegian Meteorological Institute",
"type": "dataset",
"license": "Public Domain",
"price": "123000000000000000000",
"copyrightHolder": "Norwegian Meteorological Institute",
"files": [
{
"index": 0,
"url": "https://example-url.net/weather/forecast/madrid/350750305731.xml",
"contentLength": 1000,
"contentType": "text/xml",
"compression": "none"
}
],
"categories": ["Other"],
"links": [],
"tags": []
},
"additionalInformation": {
"updateFrequency": null,
"structuredMarkup": []
}
}
}
]
}
```
NEW. Where `main` now holds the non-changable attributes only, everything else has been moved to `additionalInformation`. Likewise, `serviceDefinitionId` is now `index`:
```json
{
"service": [
{
"index": 0,
"serviceEndpoint": "http://localhost:5000/api/v1/aquarius/assets/ddo/{did}",
"type": "metadata",
"attributes": {
"main": {
"name": "Madrid Weather forecast",
"dateCreated": "2019-05-16T12:36:14.535Z",
"author": "Norwegian Meteorological Institute",
"type": "dataset",
"license": "Public Domain",
"price": "123000000000000000000",
"files": [
{
"index": 0,
"url": "https://example-url.net/weather/forecast/madrid/350750305731.xml",
"contentLength": "1000",
"contentType": "text/xml",
"compression": "none"
}
]
},
"additionalInformation": {
"description": "Weather forecast of Europe/Madrid in XML format",
"copyrightHolder": "Norwegian Meteorological Institute",
"categories": ["Other"],
"links": [],
"tags": [],
"updateFrequency": null,
"structuredMarkup": []
}
}
}
]
}
```
Those changes require updates to your code whenever you fetch or create a new DDO as outlined below.
Likewise, you have to migrate the DDOs of existing registered assets to the new structure. For this you can run our migration script on your respective Aquarius instance.
- [script: CHANGEME](https://github.com/oceanprotocol/CHANGEME)
### Lowercase Service Names
All the service names are now in lowercase:
old:
```js
const service = ddo.findServiceByType('Access')
```
NEW:
```js
const service = ddo.findServiceByType('access')
```
### Accessing New Metadata Structure
Typically you would grab and restructure asset metadata for display. This works the same as before but the key names have changed:
old:
```js
const { metadata } = ddo.findServiceByType('Metadata')
const { base, additionalInformation } = metadata
console.log(base.price)
console.log(base.description)
```
NEW:
```js
const { attributes } = ddo.findServiceByType('metadata')
const { main, additionalInformation } = attributes
console.log(main.price)
console.log(additionalInformation.description)
```
### `service.serviceDefinitionId``service.index`
old:
```js
await ocean.assets.order(ddo.id, service.serviceDefinitionId, accounts[0])
```
NEW:
```js
await ocean.assets.order(ddo.id, service.index, accounts[0])
```
The value of of `service.index` is now also a number, and not a string.
### File Attribute Changes
In the file attributes, the `contentLength` is now a string.
old:
```json
"files": [
{
"index": 0,
"url": "https://example-url.net/weather/forecast/madrid/350750305731.xml",
"contentLength": 1000,
"contentType": "text/xml"
}
]
```
NEW:
```json
"files": [
{
"index": 0,
"url": "https://example-url.net/weather/forecast/madrid/350750305731.xml",
"contentLength": "1000",
"contentType": "text/xml"
}
]
```

View File

@ -21,6 +21,7 @@
- [Get started](#get-started) - [Get started](#get-started)
- [Examples](#examples) - [Examples](#examples)
- [Documentation](#documentation) - [Documentation](#documentation)
- [Migration Guide](#migration-guide)
- [Development](#development) - [Development](#development)
- [Testing](#testing) - [Testing](#testing)
- [Unit Tests](#unit-tests) - [Unit Tests](#unit-tests)
@ -75,6 +76,7 @@ For an overview of endpoint configurations making up various Ocean networks, ple
You can see how `squid-js` is used on: You can see how `squid-js` is used on:
- [Docs: React Tutorial](https://docs.oceanprotocol.com/tutorials/react-setup/)
- [Integration test](/src/integration/ocean/) - [Integration test](/src/integration/ocean/)
- [Tuna](https://github.com/oceanprotocol/tuna/tree/develop/node) - [Tuna](https://github.com/oceanprotocol/tuna/tree/develop/node)
@ -89,6 +91,12 @@ Alternatively, you can generate the raw TypeDoc documentation locally by running
npm run doc npm run doc
``` ```
### Migration Guide
Instructions on how to migrate between breaking versions:
- [Migration Guide](MIGRATION.md)
## Development ## Development
To start development you need to: To start development you need to:

View File

@ -1 +0,0 @@
this document has been moved to [here](https://github.com/oceanprotocol/dev-ocean/blob/master/doc/architecture/squid.md).

View File

@ -1,9 +1,9 @@
import { Config } from '../src' import { Config } from '../src'
const HDWalletProvider = require('@truffle/hdwallet-provider') import HDWalletProvider from '@truffle/hdwallet-provider'
const configJson: Config = { const configJson: Config = {
nodeUri: 'http://localhost:8545', nodeUri: 'http://localhost:8545',
aquariusUri: 'http://172.15.0.15:5000', aquariusUri: 'http://aquarius:5000',
brizoUri: 'http://localhost:8030', brizoUri: 'http://localhost:8030',
secretStoreUri: 'http://localhost:12001', secretStoreUri: 'http://localhost:12001',
brizoAddress: '0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0', brizoAddress: '0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0',
@ -44,12 +44,7 @@ if (process.env.SEED_WORDS) {
const seedphrase = process.env.SEED_WORDS const seedphrase = process.env.SEED_WORDS
// @ts-ignore // @ts-ignore
configJson.web3Provider = new HDWalletProvider( configJson.web3Provider = new HDWalletProvider(seedphrase, configJson.nodeUri, 0, 5)
seedphrase,
configJson.nodeUri,
0,
5
)
} }
export const config: Config & { forceVerbose: Config } = configJson as any export const config: Config & { forceVerbose: Config } = configJson as any

View File

@ -40,19 +40,33 @@ describe('Asset Owners', () => {
assert.isTrue(isProvider) assert.isTrue(isProvider)
}) })
it('should get the assets owned by a user', async () => { it('should be added correctly a permission on an asset', async () => {
const { length: initialLength } = await ocean.assets.ownerAssets( const ddo = await ocean.assets.create(metadata as any, account1)
account2.getId()
assert.isFalse(
await ocean.keeper.didRegistry.getPermission(ddo.id, account2.getId())
) )
await ocean.keeper.didRegistry.grantPermission(
ddo.id,
account2.getId(),
account1.getId()
)
assert.isTrue(
await ocean.keeper.didRegistry.getPermission(ddo.id, account2.getId())
)
})
it('should get the assets owned by a user', async () => {
const { length: initialLength } = await ocean.assets.ownerAssets(account2.getId())
await ocean.assets.create(metadata as any, account1) await ocean.assets.create(metadata as any, account1)
await ocean.assets.create(metadata as any, account1) await ocean.assets.create(metadata as any, account1)
await ocean.assets.create(metadata as any, account2) await ocean.assets.create(metadata as any, account2)
const { length: finalLength } = await ocean.assets.ownerAssets( const { length: finalLength } = await ocean.assets.ownerAssets(account2.getId())
account2.getId()
)
assert.equal(finalLength - initialLength, 1) assert.equal(finalLength - initialLength, 1)
}) })
@ -72,16 +86,13 @@ describe('Asset Owners', () => {
// Granting access // Granting access
try { try {
await account2.requestTokens( await account2.requestTokens(
+metadata.base.price * +metadata.main.price * 10 ** -(await ocean.keeper.token.decimals())
10 ** -(await ocean.keeper.token.decimals())
) )
} catch {} } catch {}
await ocean.assets.order( const { index } = ddo.findServiceByType('access')
ddo.id,
ddo.findServiceByType('Access').serviceDefinitionId, await ocean.assets.order(ddo.id, index, account2)
account2
)
// Access granted // Access granted
const { length: finalLength2 } = await ocean.assets.consumerAssets( const { length: finalLength2 } = await ocean.assets.consumerAssets(
@ -89,4 +100,14 @@ describe('Asset Owners', () => {
) )
assert.equal(finalLength2 - initialLength, 1) assert.equal(finalLength2 - initialLength, 1)
}) })
it('should be able to transfer ownership', async () => {
const { id } = await ocean.assets.create(metadata as any, account1)
// transfer
await ocean.assets.transferOwnership(id, account2.getId())
const newOwner = await ocean.keeper.didRegistry.getDIDOwner(id)
assert.equal(newOwner, account2.getId())
})
}) })

View File

@ -19,6 +19,12 @@ describe('Authentication Token', () => {
;[account1, account2] = await ocean.accounts.list() ;[account1, account2] = await ocean.accounts.list()
}) })
after(async () => {
try {
localStorage.clear()
} catch {}
})
it('should generate a token', async () => { it('should generate a token', async () => {
const token = await ocean.auth.get(account1) const token = await ocean.auth.get(account1)

View File

@ -31,26 +31,22 @@ describe('Consume Asset', () => {
} }
}) })
it('should regiester a asset', async () => { it('should register an asset', async () => {
ddo = await ocean.assets.create(metadata as any, publisher) ddo = await ocean.assets.create(metadata as any, publisher)
assert.isDefined(ddo, 'Register has not returned a DDO') assert.isDefined(ddo, 'Register has not returned a DDO')
assert.match(ddo.id, /^did:op:[a-f0-9]{64}$/, 'DDO id is not valid') assert.match(ddo.id, /^did:op:[a-f0-9]{64}$/, 'DDO id is not valid')
assert.isAtLeast( assert.isAtLeast(ddo.authentication.length, 1, 'Default authentication not added')
ddo.authentication.length,
1,
'Default authentication not added'
)
assert.isDefined( assert.isDefined(
ddo.findServiceByType('Access'), ddo.findServiceByType('access'),
"DDO Access service doesn't exist" "DDO access service doesn't exist"
) )
}) })
it('should be able to request tokens for consumer', async () => { it('should be able to request tokens for consumer', async () => {
const initialBalance = (await consumer.getBalance()).ocn const initialBalance = (await consumer.getBalance()).ocn
const claimedTokens = const claimedTokens =
+metadata.base.price * 10 ** -(await ocean.keeper.token.decimals()) +metadata.main.price * 10 ** -(await ocean.keeper.token.decimals())
try { try {
await consumer.requestTokens(claimedTokens) await consumer.requestTokens(claimedTokens)
@ -64,11 +60,11 @@ describe('Consume Asset', () => {
}) })
it('should sign the service agreement', async () => { it('should sign the service agreement', async () => {
const accessService = ddo.findServiceByType('Access') const accessService = ddo.findServiceByType('access')
serviceAgreementSignatureResult = await ocean.agreements.prepare( serviceAgreementSignatureResult = await ocean.agreements.prepare(
ddo.id, ddo.id,
accessService.serviceDefinitionId, accessService.index,
consumer consumer
) )
@ -86,12 +82,12 @@ describe('Consume Asset', () => {
}) })
it('should execute the service agreement', async () => { it('should execute the service agreement', async () => {
const accessService = ddo.findServiceByType('Access') const accessService = ddo.findServiceByType('access')
const success = await ocean.agreements.create( const success = await ocean.agreements.create(
ddo.id, ddo.id,
serviceAgreementSignatureResult.agreementId, serviceAgreementSignatureResult.agreementId,
accessService.serviceDefinitionId, accessService.index,
serviceAgreementSignatureResult.signature, serviceAgreementSignatureResult.signature,
consumer, consumer,
publisher publisher
@ -115,7 +111,7 @@ describe('Consume Asset', () => {
it('should lock the payment by the consumer', async () => { it('should lock the payment by the consumer', async () => {
const paid = await ocean.agreements.conditions.lockReward( const paid = await ocean.agreements.conditions.lockReward(
serviceAgreementSignatureResult.agreementId, serviceAgreementSignatureResult.agreementId,
ddo.findServiceByType('Metadata').metadata.base.price, ddo.findServiceByType('metadata').attributes.main.price,
consumer consumer
) )
@ -156,13 +152,13 @@ describe('Consume Asset', () => {
}) })
it('should consume and store the assets', async () => { it('should consume and store the assets', async () => {
const accessService = ddo.findServiceByType('Access') const accessService = ddo.findServiceByType('access')
const folder = '/tmp/ocean/squid-js-1' const folder = '/tmp/ocean/squid-js-1'
const path = await ocean.assets.consume( const path = await ocean.assets.consume(
serviceAgreementSignatureResult.agreementId, serviceAgreementSignatureResult.agreementId,
ddo.id, ddo.id,
accessService.serviceDefinitionId, accessService.index,
consumer, consumer,
folder folder
) )
@ -182,14 +178,14 @@ describe('Consume Asset', () => {
) )
}) })
it('should consume and store one assets', async () => { it('should consume and store one asset', async () => {
const accessService = ddo.findServiceByType('Access') const accessService = ddo.findServiceByType('access')
const folder = '/tmp/ocean/squid-js-2' const folder = '/tmp/ocean/squid-js-2'
const path = await ocean.assets.consume( const path = await ocean.assets.consume(
serviceAgreementSignatureResult.agreementId, serviceAgreementSignatureResult.agreementId,
ddo.id, ddo.id,
accessService.serviceDefinitionId, accessService.index,
consumer, consumer,
folder, folder,
1 1

View File

@ -50,18 +50,17 @@ describe('Consume Asset (Brizo)', () => {
}) })
it('should order the asset', async () => { it('should order the asset', async () => {
const accessService = ddo.findServiceByType('Access') const accessService = ddo.findServiceByType('access')
try { try {
await consumer.requestTokens( await consumer.requestTokens(
+metadata.base.price * +metadata.main.price * 10 ** -(await ocean.keeper.token.decimals())
10 ** -(await ocean.keeper.token.decimals())
) )
} catch {} } catch {}
const steps = [] const steps = []
agreementId = await ocean.assets agreementId = await ocean.assets
.order(ddo.id, accessService.serviceDefinitionId, consumer) .order(ddo.id, accessService.index, consumer)
.next(step => steps.push(step)) .next(step => steps.push(step))
assert.isDefined(agreementId) assert.isDefined(agreementId)
@ -69,13 +68,13 @@ describe('Consume Asset (Brizo)', () => {
}) })
it('should consume and store the assets', async () => { it('should consume and store the assets', async () => {
const accessService = ddo.findServiceByType('Access') const accessService = ddo.findServiceByType('access')
const folder = '/tmp/ocean/squid-js' const folder = '/tmp/ocean/squid-js'
const path = await ocean.assets.consume( const path = await ocean.assets.consume(
agreementId, agreementId,
ddo.id, ddo.id,
accessService.serviceDefinitionId, accessService.index,
consumer, consumer,
folder folder
) )

View File

@ -30,10 +30,12 @@ xdescribe('Consume Asset (Large size)', () => {
} }
metadata = { metadata = {
...baseMetadata, ...baseMetadata,
base: { main: {
...baseMetadata.base, ...baseMetadata.main,
files: [ files: [
{ {
index: 0,
contentType: 'hello/hello',
url: 'https://speed.hetzner.de/1GB.bin' url: 'https://speed.hetzner.de/1GB.bin'
} }
] ]
@ -48,32 +50,27 @@ xdescribe('Consume Asset (Large size)', () => {
}) })
it('should order the asset', async () => { it('should order the asset', async () => {
const accessService = ddo.findServiceByType('Access') const accessService = ddo.findServiceByType('access')
try { try {
await consumer.requestTokens( await consumer.requestTokens(
+metadata.base.price * +metadata.main.price * 10 ** -(await ocean.keeper.token.decimals())
10 ** -(await ocean.keeper.token.decimals())
) )
} catch {} } catch {}
agreementId = await ocean.assets.order( agreementId = await ocean.assets.order(ddo.id, accessService.index, consumer)
ddo.id,
accessService.serviceDefinitionId,
consumer
)
assert.isDefined(agreementId) assert.isDefined(agreementId)
}) })
it('should consume and store the assets', async () => { it('should consume and store the assets', async () => {
const accessService = ddo.findServiceByType('Access') const accessService = ddo.findServiceByType('access')
const folder = '/tmp/ocean/squid-js' const folder = '/tmp/ocean/squid-js'
const path = await ocean.assets.consume( const path = await ocean.assets.consume(
agreementId, agreementId,
ddo.id, ddo.id,
accessService.serviceDefinitionId, accessService.index,
consumer, consumer,
folder folder
) )

View File

@ -4,11 +4,7 @@ import { config } from '../config'
import { Ocean, templates, conditions, utils, Account, Keeper } from '../../src' // @oceanprotocol/squid import { Ocean, templates, conditions, utils, Account, Keeper } from '../../src' // @oceanprotocol/squid
const { const { LockRewardCondition, EscrowReward, AccessSecretStoreCondition } = conditions
LockRewardCondition,
EscrowReward,
AccessSecretStoreCondition
} = conditions
describe('Register Escrow Access Secret Store Template', () => { describe('Register Escrow Access Secret Store Template', () => {
let ocean: Ocean let ocean: Ocean
@ -40,8 +36,7 @@ describe('Register Escrow Access Secret Store Template', () => {
consumer = (await ocean.accounts.list())[2] consumer = (await ocean.accounts.list())[2]
// Conditions // Conditions
accessSecretStoreCondition = accessSecretStoreCondition = keeper.conditions.accessSecretStoreCondition
keeper.conditions.accessSecretStoreCondition
lockRewardCondition = keeper.conditions.lockRewardCondition lockRewardCondition = keeper.conditions.lockRewardCondition
escrowReward = keeper.conditions.escrowReward escrowReward = keeper.conditions.escrowReward

View File

@ -0,0 +1,300 @@
import { assert } from 'chai'
import { config } from '../config'
import { Ocean, templates, conditions, utils, Account, Keeper } from '../../src' // @oceanprotocol/squid
const { LockRewardCondition, EscrowReward, ComputeExecutionCondition } = conditions
describe('Register Escrow Compute Execution Template', () => {
let ocean: Ocean
let keeper: Keeper
let template: templates.EscrowComputeExecutionTemplate
const url = 'https://example.com/did/ocean/test-attr-example.txt'
const checksum = 'b'.repeat(32)
let escrowAmount = 12
let templateManagerOwner: Account
let publisher: Account
let consumer: Account
let computeExecutionCondition: conditions.ComputeExecutionCondition
let lockRewardCondition: conditions.LockRewardCondition
let escrowReward: conditions.EscrowReward
before(async () => {
ocean = await Ocean.getInstance(config)
keeper = ocean.keeper
template = keeper.templates.escrowComputeExecutionTemplate
// Accounts
templateManagerOwner = (await ocean.accounts.list())[0]
publisher = (await ocean.accounts.list())[1]
consumer = (await ocean.accounts.list())[2]
// Conditions
computeExecutionCondition = keeper.conditions.computeExecutionCondition
lockRewardCondition = keeper.conditions.lockRewardCondition
escrowReward = keeper.conditions.escrowReward
if (!ocean.keeper.dispenser) {
escrowAmount = 0
}
})
describe('Propose and approve template', () => {
it('should propose the template', async () => {
await keeper.templateStoreManager.proposeTemplate(
template.getAddress(),
consumer.getId(),
true
)
// TODO: Use a event to detect template mined
await new Promise(resolve => setTimeout(resolve, 2 * 1000))
})
it('should approve the template', async () => {
await keeper.templateStoreManager.approveTemplate(
template.getAddress(),
templateManagerOwner.getId(),
true
)
// TODO: Use a event to detect template mined
await new Promise(resolve => setTimeout(resolve, 2 * 1000))
})
})
describe('Full flow', () => {
const agreementId = `0x${utils.generateId()}`
const did = `0x${utils.generateId()}`
let conditionIdCompute: string
let conditionIdLock: string
let conditionIdEscrow: string
it('should register a DID', async () => {
await keeper.didRegistry.registerAttribute(
did,
checksum,
[],
url,
publisher.getId()
)
})
it('should generate the condition IDs', async () => {
conditionIdCompute = await computeExecutionCondition.generateIdHash(
agreementId,
did,
consumer.getId()
)
conditionIdLock = await lockRewardCondition.generateIdHash(
agreementId,
await escrowReward.getAddress(),
escrowAmount
)
conditionIdEscrow = await escrowReward.generateIdHash(
agreementId,
escrowAmount,
publisher.getId(),
consumer.getId(),
conditionIdLock,
conditionIdCompute
)
})
it('should have conditions types', async () => {
const conditionTypes = await template.getConditionTypes()
assert.equal(conditionTypes.length, 3, 'Expected 3 conditions.')
assert.deepEqual(
[...conditionTypes].sort(),
[
computeExecutionCondition.getAddress(),
escrowReward.getAddress(),
lockRewardCondition.getAddress()
].sort(),
"The conditions doesn't match"
)
})
it('should have condition instances asociated', async () => {
const conditionInstances = await template.getConditions()
assert.equal(conditionInstances.length, 3, 'Expected 3 conditions.')
const conditionClasses = [
ComputeExecutionCondition,
EscrowReward,
LockRewardCondition
]
conditionClasses.forEach(conditionClass => {
if (
!conditionInstances.find(
condition => condition instanceof conditionClass
)
) {
throw new Error(
`${conditionClass.name} is not part of the conditions.`
)
}
})
})
it('should create a new agreement', async () => {
const agreement = await template.createAgreement(
agreementId,
did,
[conditionIdCompute, conditionIdLock, conditionIdEscrow],
[0, 0, 0],
[0, 0, 0],
consumer.getId(),
publisher.getId()
)
assert.isTrue(agreement.status)
})
it('should not trigger the compute', async () => {
const computeTriggered = await computeExecutionCondition.wasComputeTriggered(
did,
consumer.getId()
)
assert.isFalse(computeTriggered, 'Compute has been triggered.')
})
it('should fulfill LockRewardCondition', async () => {
try {
await consumer.requestTokens(escrowAmount)
} catch {}
await keeper.token.approve(
lockRewardCondition.getAddress(),
escrowAmount,
consumer.getId()
)
const fulfill = await lockRewardCondition.fulfill(
agreementId,
escrowReward.getAddress(),
escrowAmount,
consumer.getId()
)
assert.isDefined(fulfill.events.Fulfilled, 'Not Fulfilled event.')
})
it('should fulfill ComputeExecutionCondition', async () => {
const fulfill = await computeExecutionCondition.fulfill(
agreementId,
did,
consumer.getId(),
publisher.getId()
)
assert.isDefined(fulfill.events.Fulfilled, 'Not Fulfilled event.')
})
it('should fulfill EscrowReward', async () => {
const fulfill = await escrowReward.fulfill(
agreementId,
escrowAmount,
publisher.getId(),
consumer.getId(),
conditionIdLock,
conditionIdCompute,
consumer.getId()
)
assert.isDefined(fulfill.events.Fulfilled, 'Not Fulfilled event.')
})
it('should grant the access to the consumer', async () => {
const computeTriggered = await computeExecutionCondition.wasComputeTriggered(
did,
consumer.getId()
)
assert.isTrue(computeTriggered, 'Compute has not been triggered.')
})
})
describe('Short flow', () => {
const did = utils.generateId()
let agreementId
it('should register a DID', async () => {
// This part is executed inside Ocean.assets.create()
await keeper.didRegistry.registerAttribute(
did,
checksum,
[],
url,
publisher.getId()
)
})
it('should create a new agreement (short way)', async () => {
agreementId = await template.createFullAgreement(
did,
escrowAmount,
consumer.getId(),
publisher.getId()
)
assert.match(agreementId, /^0x[a-f0-9]{64}$/i)
})
it('should not grant the access to the consumer', async () => {
const computeTriggered = await computeExecutionCondition.wasComputeTriggered(
did,
consumer.getId()
)
assert.isFalse(computeTriggered, 'Compute has been triggered.')
})
it('should fulfill the conditions from consumer side', async () => {
try {
await consumer.requestTokens(escrowAmount)
} catch {}
await ocean.agreements.conditions.lockReward(
agreementId,
escrowAmount,
consumer
)
})
it('should fulfill the conditions from computing side', async () => {
await ocean.agreements.conditions.grantServiceExecution(
agreementId,
did,
consumer.getId(),
publisher
)
await ocean.agreements.conditions.releaseReward(
agreementId,
escrowAmount,
did,
consumer.getId(),
publisher.getId(),
publisher
)
})
it('should grant the access to the consumer', async () => {
const computeTriggered = await computeExecutionCondition.wasComputeTriggered(
did,
consumer.getId()
)
assert.isTrue(computeTriggered, 'Compute has not been triggered.')
})
})
})

View File

@ -35,57 +35,41 @@ describe('Search Asset', () => {
assert.isArray(ddos, 'A search should return an array') assert.isArray(ddos, 'A search should return an array')
test1length = ddos.length test1length = ddos.length
test2length = (await ocean.assets.search(`Test2${testHash}`)).results test2length = (await ocean.assets.search(`Test2${testHash}`)).results.length
.length test3length = (await ocean.assets.search(`Test3${testHash}`)).results.length
test3length = (await ocean.assets.search(`Test3${testHash}`)).results
.length
if (!ocean.keeper.dispenser) { if (!ocean.keeper.dispenser) {
price = 0 price = 0
} }
}) })
it('should regiester some a asset', async () => { it('should register an asset', async () => {
assert.instanceOf( assert.instanceOf(
await ocean.assets.create( await ocean.assets.create(metadataGenerator('Test1') as any, publisher),
metadataGenerator('Test1') as any,
publisher
),
DDO DDO
) )
assert.instanceOf( assert.instanceOf(
await ocean.assets.create( await ocean.assets.create(metadataGenerator('Test2') as any, publisher),
metadataGenerator('Test2') as any,
publisher
),
DDO DDO
) )
assert.instanceOf( assert.instanceOf(
await ocean.assets.create( await ocean.assets.create(metadataGenerator('Test2') as any, publisher),
metadataGenerator('Test2') as any,
publisher
),
DDO DDO
) )
assert.instanceOf( assert.instanceOf(
await ocean.assets.create( await ocean.assets.create(metadataGenerator('Test3') as any, publisher),
metadataGenerator('Test3') as any,
publisher
),
DDO DDO
) )
}) })
it('should search by text and see the increment of DDOs', async () => { it('should search by text and see the increment of DDOs', async () => {
assert.equal( assert.equal(
(await ocean.assets.search(`Test2${testHash}`)).results.length - (await ocean.assets.search(`Test2${testHash}`)).results.length - test2length,
test2length,
2, 2,
'Something was wrong searching the assets' 'Something was wrong searching the assets'
) )
assert.equal( assert.equal(
(await ocean.assets.search(`Test3${testHash}`)).results.length - (await ocean.assets.search(`Test3${testHash}`)).results.length - test3length,
test3length,
1, 1,
'Something was wrong searching the assets' 'Something was wrong searching the assets'
) )

View File

@ -21,11 +21,7 @@ describe('Secret Store', () => {
}) })
it('should encrypt a text', async () => { it('should encrypt a text', async () => {
encryptedContent = await ocean.secretStore.encrypt( encryptedContent = await ocean.secretStore.encrypt(did.getId(), content, account)
did.getId(),
content,
account
)
assert.isDefined(encryptedContent) assert.isDefined(encryptedContent)
assert.match(encryptedContent, /^0x[a-f0-9]{76}$/i) assert.match(encryptedContent, /^0x[a-f0-9]{76}$/i)

View File

@ -46,7 +46,6 @@ describe('Signature', () => {
const did = `did:op:${'c'.repeat(64)}` const did = `did:op:${'c'.repeat(64)}`
const templateId = `0x${'f'.repeat(40)}` const templateId = `0x${'f'.repeat(40)}`
const agreementId = `0x${'e'.repeat(64)}` const agreementId = `0x${'e'.repeat(64)}`
const serviceDefinitionId = '0'
const serviceAgreementTemplate = await templates.escrowAccessSecretStoreTemplate.getServiceAgreementTemplate() const serviceAgreementTemplate = await templates.escrowAccessSecretStoreTemplate.getServiceAgreementTemplate()
@ -54,17 +53,20 @@ describe('Signature', () => {
id: did, id: did,
service: [ service: [
{ {
type: 'Access', type: 'access',
index: 0,
purchaseEndpoint: undefined, purchaseEndpoint: undefined,
serviceEndpoint: undefined, serviceEndpoint: undefined,
serviceDefinitionId,
templateId, templateId,
serviceAgreementTemplate attributes: {
serviceAgreementTemplate
}
} as any, } as any,
{ {
type: 'Metadata', type: 'metadata',
metadata: { index: 1,
base: { attributes: {
main: {
price: 10 price: 10
} }
} }
@ -74,13 +76,9 @@ describe('Signature', () => {
const signature = await ocean.utils.agreements.signServiceAgreement( const signature = await ocean.utils.agreements.signServiceAgreement(
ddo, ddo,
serviceDefinitionId, 0,
agreementId, agreementId,
[ [`0x${'1'.repeat(64)}`, `0x${'2'.repeat(64)}`, `0x${'3'.repeat(64)}`],
`0x${'1'.repeat(64)}`,
`0x${'2'.repeat(64)}`,
`0x${'3'.repeat(64)}`
],
consumer consumer
) )

View File

@ -11,7 +11,8 @@ describe('Versions', () => {
ocean = await Ocean.getInstance(config) ocean = await Ocean.getInstance(config)
}) })
it('should returns the versions', async () => { // TODO: enable again after new versions of Brizo
xit('should return the versions', async () => {
const versions = await ocean.versions.get() const versions = await ocean.versions.get()
assert.equal(versions.aquarius.status, OceanPlatformTechStatus.Working) assert.equal(versions.aquarius.status, OceanPlatformTechStatus.Working)

View File

@ -2,6 +2,8 @@
"compilerOptions": { "compilerOptions": {
"resolveJsonModule": true, "resolveJsonModule": true,
"lib": ["es6", "es7", "dom"], "lib": ["es6", "es7", "dom"],
"noUnusedLocals": true "noUnusedLocals": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
} }
} }

View File

@ -1,18 +1,33 @@
import { MetaData } from '../../src' // @oceanprotocol/squid import { MetaData } from '../../src' // @oceanprotocol/squid
const metadata: Partial<MetaData> = { const metadata: Partial<MetaData> = {
base: { main: {
name: undefined, name: undefined,
type: 'dataset', type: 'dataset',
description:
'Weather information of UK including temperature and humidity',
dateCreated: '2012-10-10T17:00:00Z', dateCreated: '2012-10-10T17:00:00Z',
datePublished: '2012-10-10T17:00:00Z', datePublished: '2012-10-10T17:00:00Z',
author: 'Met Office', author: 'Met Office',
license: 'CC-BY', license: 'CC-BY',
price: '21' + '0'.repeat(18),
files: [
{
index: 0,
contentType: 'application/json',
url:
'https://raw.githubusercontent.com/oceanprotocol/squid-js/master/package.json'
},
{
index: 1,
contentType: 'text/plain',
url:
'https://raw.githubusercontent.com/oceanprotocol/squid-js/master/README.md'
}
]
},
additionalInformation: {
description: 'Weather information of UK including temperature and humidity',
copyrightHolder: 'Met Office', copyrightHolder: 'Met Office',
workExample: workExample: '423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68',
'423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68',
links: [ links: [
{ {
name: 'Sample of Asset Data', name: 'Sample of Asset Data',
@ -27,34 +42,20 @@ const metadata: Partial<MetaData> = {
], ],
inLanguage: 'en', inLanguage: 'en',
categories: ['Economy', 'Data Science'], categories: ['Economy', 'Data Science'],
tags: ['weather', 'uk', '2011', 'temperature', 'humidity'], tags: ['weather', 'uk', '2011', 'temperature', 'humidity']
price: '21' + '0'.repeat(18),
files: [
{
index: 0,
url:
'https://raw.githubusercontent.com/oceanprotocol/squid-js/master/package.json'
},
{
index: 1,
url:
'https://raw.githubusercontent.com/oceanprotocol/squid-js/master/README.md'
}
]
} }
} }
export const generateMetadata = ( export const generateMetadata = (name: string, price?: number): Partial<MetaData> => ({
name: string,
price?: number
): Partial<MetaData> => ({
...metadata, ...metadata,
base: { main: {
...metadata.base, ...metadata.main,
name, name,
price: (price || 21) + '0'.repeat(18) price: (price || 21) + '0'.repeat(18)
},
additionalInformation: {
...metadata.additionalInformation
} }
}) })
export const getMetadata = (price?: number) => export const getMetadata = (price?: number) => generateMetadata('TestAsset', price)
generateMetadata('TestAsset', price)

View File

@ -11,15 +11,15 @@
}, },
{ {
"name": "brizo", "name": "brizo",
"version": "~0.4.5" "version": "~0.7.2"
}, },
{ {
"name": "aquarius", "name": "aquarius",
"version": "~0.3.8" "version": "~1.0.5"
}, },
{ {
"name": "events-handler", "name": "events-handler",
"version": "~0.1.2" "version": "~0.3.4"
} }
] ]
} }

6142
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@oceanprotocol/squid", "name": "@oceanprotocol/squid",
"version": "0.8.3", "version": "1.0.0-beta.7",
"description": "JavaScript client library for Ocean Protocol", "description": "JavaScript client library for Ocean Protocol",
"main": "./dist/node/squid.js", "main": "./dist/node/squid.js",
"typings": "./dist/node/squid.d.ts", "typings": "./dist/node/squid.d.ts",
@ -49,7 +49,7 @@
}, },
"homepage": "https://github.com/oceanprotocol/squid-js#readme", "homepage": "https://github.com/oceanprotocol/squid-js#readme",
"peerDependencies": { "peerDependencies": {
"web3": "^1.2.0" "web3": "^1.2.3"
}, },
"dependencies": { "dependencies": {
"@oceanprotocol/keeper-contracts": "^0.12.7", "@oceanprotocol/keeper-contracts": "^0.12.7",
@ -59,37 +59,37 @@
"node-fetch": "^2.6.0", "node-fetch": "^2.6.0",
"save-file": "^2.3.1", "save-file": "^2.3.1",
"uuid": "^3.3.3", "uuid": "^3.3.3",
"web3": "1.2.1", "web3": "^1.2.4",
"whatwg-url": "^7.1.0" "whatwg-url": "^7.1.0"
}, },
"devDependencies": { "devDependencies": {
"@release-it/bumper": "^1.0.5", "@release-it/bumper": "^1.0.5",
"@truffle/hdwallet-provider": "^1.0.23", "@truffle/hdwallet-provider": "^1.0.26",
"@types/chai": "^4.2.4", "@types/chai": "^4.2.6",
"@types/chai-spies": "^1.0.1", "@types/chai-spies": "^1.0.1",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "^12.12.5", "@types/node": "^12.12.14",
"@types/node-fetch": "^2.5.3", "@types/node-fetch": "^2.5.4",
"@typescript-eslint/eslint-plugin": "^2.6.0", "@typescript-eslint/eslint-plugin": "^2.10.0",
"@typescript-eslint/parser": "^2.6.0", "@typescript-eslint/parser": "^2.10.0",
"auto-changelog": "^1.16.2", "auto-changelog": "^1.16.2",
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-spies": "^1.0.0", "chai-spies": "^1.0.0",
"cross-env": "^6.0.3", "cross-env": "^6.0.3",
"eslint": "^6.6.0", "eslint": "^6.7.2",
"eslint-config-oceanprotocol": "^1.5.0", "eslint-config-oceanprotocol": "^1.5.0",
"eslint-config-prettier": "^6.5.0", "eslint-config-prettier": "^6.7.0",
"eslint-plugin-prettier": "^3.1.1", "eslint-plugin-prettier": "^3.1.1",
"lcov-result-merger": "^3.1.0", "lcov-result-merger": "^3.1.0",
"mocha": "^6.2.2", "mocha": "^6.2.2",
"mock-local-storage": "^1.1.8", "mock-local-storage": "^1.1.11",
"nyc": "^14.1.1", "nyc": "^14.1.1",
"ora": "^4.0.2", "ora": "^4.0.2",
"prettier": "^1.18.2", "prettier": "^1.19.1",
"source-map-support": "^0.5.16", "source-map-support": "^0.5.16",
"ts-node": "^8.4.1", "ts-node": "^8.5.4",
"typedoc": "^0.15.0", "typedoc": "^0.15.3",
"typescript": "^3.6.4", "typescript": "^3.7.3",
"uglifyjs-webpack-plugin": "^2.2.0", "uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.41.2", "webpack": "^4.41.2",
"webpack-cli": "^3.3.10", "webpack-cli": "^3.3.10",

View File

@ -11,7 +11,7 @@ mkdir -p artifacts
until [ $COMMAND_STATUS -eq 0 ] || [ $RETRY_COUNT -eq 120 ]; do until [ $COMMAND_STATUS -eq 0 ] || [ $RETRY_COUNT -eq 120 ]; do
keeper_contracts_docker_id=$(docker container ls | grep keeper-contracts | awk '{print $1}') keeper_contracts_docker_id=$(docker container ls | grep keeper-contracts | awk '{print $1}')
docker cp "${keeper_contracts_docker_id}":/keeper-contracts/artifacts/ready ./artifacts/ > /dev/null 2>&1 docker cp ${keeper_contracts_docker_id}:/keeper-contracts/artifacts/ready ./artifacts/ > /dev/null 2>&1
COMMAND_STATUS=$? COMMAND_STATUS=$?
sleep 5 sleep 5
(( RETRY_COUNT=RETRY_COUNT+1 )) (( RETRY_COUNT=RETRY_COUNT+1 ))

View File

@ -15,10 +15,7 @@ const outPath = './dist/squid-js.json'
const files = ['./src/squid.ts'] const files = ['./src/squid.ts']
// specifically point to tsconfig, otherwise TypeDoc fails // specifically point to tsconfig, otherwise TypeDoc fails
const config = typescript.findConfigFile( const config = typescript.findConfigFile('./tsconfig.js', typescript.sys.fileExists)
'./tsconfig.js',
typescript.sys.fileExists
)
const generateJson = () => { const generateJson = () => {
const spinnerTypedoc = ora('Generating TypeDoc json...').start() const spinnerTypedoc = ora('Generating TypeDoc json...').start()

View File

@ -1,4 +1,4 @@
import * as Web3 from 'web3' import Web3 from 'web3'
import Config from './models/Config' import Config from './models/Config'
import { Logger, LoggerInstance, LogLevel } from './utils' import { Logger, LoggerInstance, LogLevel } from './utils'
import Web3Provider from './keeper/Web3Provider' import Web3Provider from './keeper/Web3Provider'
@ -68,9 +68,7 @@ export abstract class Instantiable {
public static async getInstance(...args: any[]): Promise<any> public static async getInstance(...args: any[]): Promise<any>
public static async getInstance(config: InstantiableConfig): Promise<any> { public static async getInstance(config: InstantiableConfig): Promise<any> {
LoggerInstance.warn( LoggerInstance.warn('getInstance() methods has needs to be added to child class.')
'getInstance() methods has needs to be added to child class.'
)
} }
protected static setInstanceConfig<T extends Instantiable>( protected static setInstanceConfig<T extends Instantiable>(

View File

@ -40,26 +40,16 @@ export class Aquarius extends Instantiable {
public async getAccessUrl(accessToken: any, payload: any): Promise<string> { public async getAccessUrl(accessToken: any, payload: any): Promise<string> {
const accessUrl: string = await this.ocean.utils.fetch const accessUrl: string = await this.ocean.utils.fetch
.post( .post(`${accessToken.service_endpoint}/${accessToken.resource_id}`, payload)
`${accessToken.service_endpoint}/${accessToken.resource_id}`,
payload
)
.then((response: any): string => { .then((response: any): string => {
if (response.ok) { if (response.ok) {
return response.text() return response.text()
} }
this.logger.error( this.logger.error('Failed: ', response.status, response.statusText)
'Failed: ',
response.status,
response.statusText
)
return null return null
}) })
.then((consumptionUrl: string): string => { .then((consumptionUrl: string): string => {
this.logger.error( this.logger.error('Success accessing consume endpoint: ', consumptionUrl)
'Success accessing consume endpoint: ',
consumptionUrl
)
return consumptionUrl return consumptionUrl
}) })
.catch(error => { .catch(error => {
@ -134,10 +124,7 @@ export class Aquarius extends Instantiable {
return this.transformResult(results) return this.transformResult(results)
}) })
.catch(error => { .catch(error => {
this.logger.error( this.logger.error('Error fetching querying metadata by text: ', error)
'Error fetching querying metadata by text: ',
error
)
return this.transformResult() return this.transformResult()
}) })
@ -186,8 +173,7 @@ export class Aquarius extends Instantiable {
metadataServiceEndpoint?: string metadataServiceEndpoint?: string
): Promise<DDO> { ): Promise<DDO> {
did = did && DID.parse(did) did = did && DID.parse(did)
const fullUrl = const fullUrl = metadataServiceEndpoint || `${this.url}${apiPath}/${did.getDid()}`
metadataServiceEndpoint || `${this.url}${apiPath}/${did.getDid()}`
const result = await this.ocean.utils.fetch const result = await this.ocean.utils.fetch
.get(fullUrl) .get(fullUrl)
.then((response: any) => { .then((response: any) => {
@ -222,12 +208,7 @@ export class Aquarius extends Instantiable {
} }
private transformResult( private transformResult(
{ { results, page, total_pages: totalPages, total_results: totalResults }: any = {
results,
page,
total_pages: totalPages,
total_results: totalResults
}: any = {
result: [], result: [],
page: 0, page: 0,
total_pages: 0, // eslint-disable-line @typescript-eslint/camelcase total_pages: 0, // eslint-disable-line @typescript-eslint/camelcase

View File

@ -37,7 +37,7 @@ export class Brizo extends Instantiable {
public getComputeEndpoint( public getComputeEndpoint(
pubKey: string, pubKey: string,
serviceId: string, serviceIndex: number,
_notUsed: string, _notUsed: string,
container: string container: string
) { ) {
@ -47,14 +47,14 @@ export class Brizo extends Instantiable {
public async initializeServiceAgreement( public async initializeServiceAgreement(
did: string, did: string,
serviceAgreementId: string, serviceAgreementId: string,
serviceDefinitionId: string, serviceIndex: number,
signature: string, signature: string,
consumerAddress: string consumerAddress: string
): Promise<any> { ): Promise<any> {
const args = { const args = {
did, did,
serviceAgreementId, serviceAgreementId,
serviceDefinitionId, serviceIndex,
signature, signature,
consumerAddress consumerAddress
} }
@ -94,11 +94,7 @@ export class Brizo extends Instantiable {
consumeUrl += `&signature=${signature}` consumeUrl += `&signature=${signature}`
try { try {
await this.ocean.utils.fetch.downloadFile( await this.ocean.utils.fetch.downloadFile(consumeUrl, destination, i)
consumeUrl,
destination,
i
)
} catch (e) { } catch (e) {
this.logger.error('Error consuming assets') this.logger.error('Error consuming assets')
this.logger.error(e) this.logger.error(e)

View File

@ -0,0 +1,25 @@
export interface Provider {
type: string
description: string
environment: {
cluster: {
type: string
url: string
}
supportedContainers: {
image: string
tag: string
checksum: string
}[]
supportedServers: {
serverId: string
serverType: string
price: string
cpu: string
gpu: string
memory: string
disk: string
maxExecutionTime: number
}[]
}
}

View File

@ -1,6 +1,4 @@
import * as Web3 from 'web3'
import Web3Provider from '../keeper/Web3Provider' import Web3Provider from '../keeper/Web3Provider'
import LoggerInstance from '../utils/Logger'
import { Ocean } from '../ocean/Ocean' import { Ocean } from '../ocean/Ocean'
import { Authentication } from './Authentication' import { Authentication } from './Authentication'
import { Proof } from './Proof' import { Proof } from './Proof'
@ -53,8 +51,7 @@ export class DDO {
public constructor(ddo: Partial<DDO> = {}) { public constructor(ddo: Partial<DDO> = {}) {
Object.assign(this, ddo, { Object.assign(this, ddo, {
created: created:
(ddo && ddo.created) || (ddo && ddo.created) || new Date().toISOString().replace(/\.[0-9]{3}/, '')
new Date().toISOString().replace(/\.[0-9]{3}/, '')
}) })
} }
@ -63,20 +60,16 @@ export class DDO {
} }
/** /**
* Finds a service of a DDO by ID. * Finds a service of a DDO by index.
* @param {string} serviceDefinitionId Service ID. * @param {number} Service index.
* @return {Service} Service. * @return {Service} Service.
*/ */
public findServiceById<T extends ServiceType>( public findServiceById<T extends ServiceType>(index: number): Service<T> {
serviceDefinitionId: string if (isNaN(index)) {
): Service<T> { throw new Error('index is not set')
if (!serviceDefinitionId) {
throw new Error('serviceDefinitionId not set')
} }
const service = this.service.find( const service = this.service.find(s => s.index === index)
s => s.serviceDefinitionId === serviceDefinitionId
)
return service as Service<T> return service as Service<T>
} }
@ -86,9 +79,7 @@ export class DDO {
* @param {string} serviceType Service type. * @param {string} serviceType Service type.
* @return {Service} Service. * @return {Service} Service.
*/ */
public findServiceByType<T extends ServiceType>( public findServiceByType<T extends ServiceType>(serviceType: T): Service<T> {
serviceType: T
): Service<T> {
if (!serviceType) { if (!serviceType) {
throw new Error('serviceType not set') throw new Error('serviceType not set')
} }
@ -101,8 +92,8 @@ export class DDO {
* @return {string[]} DDO checksum. * @return {string[]} DDO checksum.
*/ */
public getChecksum(): string { public getChecksum(): string {
const { metadata } = this.findServiceByType('Metadata') const { attributes } = this.findServiceByType('metadata')
const { files, name, author, license } = metadata.base const { files, name, author, license } = attributes.main
const values = [ const values = [
...(files || []).map(({ checksum }) => checksum).filter(_ => !!_), ...(files || []).map(({ checksum }) => checksum).filter(_ => !!_),
@ -119,9 +110,9 @@ export class DDO {
/** /**
* Generates proof using personal sing. * Generates proof using personal sing.
* @param {Web3} web3 Web3 instance. * @param {Ocean} ocean Ocean instance.
* @param {string} publicKey Public key to be used on personal sign. * @param {string} publicKey Public key to be used on personal sign.
* @param {string} password Password if it's requirted. * @param {string} password Password if it's required.
* @return {Promise<Proof>} Proof object. * @return {Promise<Proof>} Proof object.
*/ */
public async generateProof( public async generateProof(
@ -145,33 +136,21 @@ export class DDO {
} }
} }
/**
* Generated and adds the checksum.
*/
public addChecksum(): void {
const metadataService = this.findServiceByType('Metadata')
if (metadataService.metadata.base.checksum) {
LoggerInstance.log('Checksum already exists')
return
}
metadataService.metadata.base.checksum = this.getChecksum()
}
/** /**
* Generates and adds a proof using personal sing on the DDO. * Generates and adds a proof using personal sing on the DDO.
* @param {Web3} web3 Web3 instance. * @param {Ocean} ocean Ocean instance.
* @param {string} publicKey Public key to be used on personal sign. * @param {string} publicKey Public key to be used on personal sign.
* @param {string} password Password if it's requirted. * @param {string} password Password if it's required.
* @return {Promise<Proof>} Proof object. * @return {Promise<Proof>} Proof object.
*/ */
public async addProof( public async addProof(
web3: Web3, ocean: Ocean,
publicKey: string, publicKey: string,
password?: string password?: string
): Promise<void> { ): Promise<void> {
if (this.proof) { if (this.proof) {
throw new Error('Proof already exists') throw new Error('Proof already exists')
} }
this.proof = await this.generateProof(web3, publicKey, password) this.proof = await this.generateProof(ocean, publicKey, password)
} }
} }

View File

@ -1,3 +1,72 @@
export interface StageRequirements {
container: {
image: string
tag: string
checksum: string
}
}
export interface StageInput {
index: number
id: string
}
export interface StageTransformation {
id: string
}
export interface StageOutput {
metadataUrl: string
secretStoreUrl: string
accessProxyUrl: string
metadata: MetaDataMain
}
export interface Stage {
index: number
stageType?: string
requirements: StageRequirements
input: StageInput
transformation: StageTransformation
output: StageOutput
}
export interface Workflow {
stages: Stage[]
}
export interface Algorithm {
language: string
format?: string
version?: string
entrypoint: string
requirements: {
requirement: string
version: string
}
}
export interface ServiceDefinition {
auth: {
type: string
user?: string
password?: string
token?: string
}
endpoints: {
index: number
url: string
method: string
contentTypes: string[]
}
}
export interface Service {
spec?: string
specChecksum?: string
definition: ServiceDefinition
}
export interface File { export interface File {
/** /**
* File name. * File name.
@ -17,6 +86,13 @@ export interface File {
*/ */
index?: number index?: number
/**
* File format, if applicable.
* @type {string}
* @example "text/csv"
*/
contentType: string
/** /**
* File checksum. * File checksum.
* @type {[type]} * @type {[type]}
@ -33,7 +109,7 @@ export interface File {
* File content length. * File content length.
* @type {[type]} * @type {[type]}
*/ */
contentLength?: number contentLength?: string
/** /**
* Resource ID (depending on the source). * Resource ID (depending on the source).
@ -54,20 +130,13 @@ export interface File {
* @example "zip" * @example "zip"
*/ */
compression?: string compression?: string
/**
* File format, if applicable.
* @type {string}
* @example "text/csv"
*/
contentType?: string
} }
/** /**
* Base attributes of Assets Metadata. * Main attributes of assets metadata.
* @see https://github.com/oceanprotocol/OEPs/tree/master/8#base-attributes * @see https://github.com/oceanprotocol/OEPs/tree/master/8
*/ */
export interface MetaDataBase { export interface MetaDataMain {
/** /**
* Descriptive name of the Asset. * Descriptive name of the Asset.
* @type {string} * @type {string}
@ -83,14 +152,6 @@ export interface MetaDataBase {
*/ */
type: 'dataset' | 'algorithm' | 'container' | 'workflow' | 'other' type: 'dataset' | 'algorithm' | 'container' | 'workflow' | 'other'
/**
* Details of what the resource is. For a dataset, this attribute
* explains what the data represents and what it can be used for.
* @type {string}
* @example "Weather information of UK including temperature and humidity"
*/
description?: string
/** /**
* The date on which the asset was created by the originator in * The date on which the asset was created by the originator in
* ISO 8601 format, Coordinated Universal Time. * ISO 8601 format, Coordinated Universal Time.
@ -123,6 +184,75 @@ export interface MetaDataBase {
*/ */
license: string license: string
/**
* Price of the asset.
* @type {string}
* @example "1000000000000000000"
*/
price: string
/**
* Array of File objects including the encrypted file urls and some additional information.
* @type {File[]}
*/
files: File[]
encryptedService?: any
workflow?: Workflow
algorithm?: Algorithm
service?: Service
}
/**
* Curation attributes of Assets Metadata.
* @see https://github.com/oceanprotocol/OEPs/tree/master/8
*/
export interface Curation {
/**
* Decimal value between 0 and 1. 0 is the default value.
* @type {number}
* @example 0.93
*/
rating: number
/**
* Number of votes. 0 is the default value.
* @type {number}
* @example 123
*/
numVotes: number
/**
* Schema applied to calculate the rating.
* @type {string}
* @example "Binary Voting"
*/
schema?: string
/**
* Flag unsuitable content.
* @type {boolean}
* @example true
*/
isListed?: boolean
}
/**
* Additional Information of Assets Metadata.
* @see https://github.com/oceanprotocol/OEPs/tree/master/8#additional-information
*/
export interface AdditionalInformation {
/**
* Details of what the resource is. For a dataset, this attribute
* explains what the data represents and what it can be used for.
* @type {string}
* @example "Weather information of UK including temperature and humidity"
*/
description?: string
/** /**
* The party holding the legal copyright. Empty by default. * The party holding the legal copyright. Empty by default.
* @type {string} * @type {string}
@ -179,60 +309,6 @@ export interface MetaDataBase {
*/ */
tags?: string[] tags?: string[]
/**
* Price of the asset.
* @type {string}
* @example "1000000000000000000"
*/
price: string
/**
* Array of File objects including the encrypted file urls and some additional information.
* @type {File[]}
*/
files: File[]
/**
* SHA3 hash of concatenated values: [list of all file checksums] + name + author + license + did
* @type {string}
*/
checksum?: string
encryptedFiles?: any
}
/**
* Curation attributes of Assets Metadata.
* @see https://github.com/oceanprotocol/OEPs/tree/master/8#curation-attributes
*/
export interface Curation {
/**
* Decimal value between 0 and 1. 0 is the default value.
* @type {number}
* @example 0.93
*/
rating: number
/**
* Number of votes. 0 is the default value.
* @type {number}
* @example 123
*/
numVotes: number
/**
* Schema applied to calculate the rating.
* @type {string}
* @example "Binary Voting"
*/
schema?: string
}
/**
* Additional Information of Assets Metadata.
* @see https://github.com/oceanprotocol/OEPs/tree/master/8#additional-information
*/
export interface AdditionalInformation {
/** /**
* An indication of update latency - i.e. How often are updates expected (seldom, * An indication of update latency - i.e. How often are updates expected (seldom,
* annually, quarterly, etc.), or is the resource static that is never expected * annually, quarterly, etc.), or is the resource static that is never expected
@ -240,28 +316,22 @@ export interface AdditionalInformation {
* @type {string} * @type {string}
* @example "yearly" * @example "yearly"
*/ */
updateFrequency: string updateFrequency?: string
/** /**
* A link to machine-readable structured markup (such as ttl/json-ld/rdf) * A link to machine-readable structured markup (such as ttl/json-ld/rdf)
* describing the dataset. * describing the dataset.
* @type {StructuredMarkup[]} * @type {StructuredMarkup[]}
*/ */
structuredMarkup: { structuredMarkup?: {
uri: string uri: string
mediaType: string mediaType: string
}[] }[]
/**
* Checksum of attributes to be able to compare if there are changes in
* the asset that you are purchasing.
* @type {string}
*/
checksum: string
} }
export interface MetaData { export interface MetaData {
main: MetaDataMain
encryptedFiles?: string
additionalInformation?: AdditionalInformation additionalInformation?: AdditionalInformation
base: MetaDataBase
curation?: Curation curation?: Curation
} }

View File

@ -1,36 +1,56 @@
import { MetaData } from './MetaData' import { MetaData } from './MetaData'
import { ServiceAgreementTemplate } from './ServiceAgreementTemplate' import { ServiceAgreementTemplate } from './ServiceAgreementTemplate'
import { Provider } from './ComputingProvider'
export type ServiceType = export type ServiceType =
| 'Authorization' | 'authorization'
| 'Metadata' | 'metadata'
| 'Access' | 'access'
| 'Compute' | 'compute'
| 'FitchainCompute' | 'computing'
| 'fitchainCompute'
export interface ServiceCommon { export interface ServiceCommon {
type: ServiceType type: ServiceType
serviceDefinitionId?: string index: number
serviceEndpoint?: string serviceEndpoint?: string
attributes: any & {
main: { [key: string]: any }
}
} }
export interface ServiceAuthorization extends ServiceCommon { export interface ServiceAuthorization extends ServiceCommon {
type: 'Authorization' type: 'authorization'
service: 'SecretStore' | 'None' | 'RSAES-OAEP' service: 'SecretStore' | 'None' | 'RSAES-OAEP'
} }
export interface ServiceMetadata extends ServiceCommon { export interface ServiceMetadata extends ServiceCommon {
type: 'Metadata' type: 'metadata'
metadata: MetaData attributes: MetaData
} }
export interface ServiceAccess extends ServiceCommon { export interface ServiceAccess extends ServiceCommon {
type: 'Access' type: 'access'
name?: string
description?: string
creator?: string
templateId?: string templateId?: string
purchaseEndpoint?: string attributes: {
main: {
creator: string
name: string
datePublished: string
price: string
timeout: number
}
serviceAgreementTemplate?: ServiceAgreementTemplate
additionalInformation: {
description: string
}
}
}
export interface ServiceComputing extends ServiceCommon {
type: 'computing'
templateId?: string
provider?: Provider
serviceAgreementTemplate?: ServiceAgreementTemplate serviceAgreementTemplate?: ServiceAgreementTemplate
} }
@ -40,13 +60,15 @@ export interface ServiceCompute extends ServiceCommon {
export type Service< export type Service<
T extends ServiceType | 'default' = 'default' T extends ServiceType | 'default' = 'default'
> = T extends 'Authorization' > = T extends 'authorization'
? ServiceAuthorization ? ServiceAuthorization
: T extends 'Metadata' : T extends 'metadata'
? ServiceMetadata ? ServiceMetadata
: T extends 'Access' : T extends 'computing'
? ServiceComputing
: T extends 'access'
? ServiceAccess ? ServiceAccess
: T extends 'Compute' : T extends 'compute'
? ServiceCompute ? ServiceCompute
: T extends 'default' : T extends 'default'
? ServiceCommon ? ServiceCommon

View File

@ -17,9 +17,7 @@ export class ContractEvent {
private filter: { [key: string]: any } private filter: { [key: string]: any }
) {} ) {}
public subscribe( public subscribe(callback: (events: any[]) => void): ContractEventSubscription {
callback: (events: any[]) => void
): ContractEventSubscription {
const onEvent = async (blockNumber: number) => { const onEvent = async (blockNumber: number) => {
const events = await this.contract.getEventData(this.eventName, { const events = await this.contract.getEventData(this.eventName, {
filter: this.filter, filter: this.filter,

View File

@ -11,20 +11,14 @@ export default class ContractHandler extends Instantiable {
networkId: number, networkId: number,
contractInstance: Contract contractInstance: Contract
) { ) {
ContractHandler.contracts.set( ContractHandler.contracts.set(this.getHash(what, networkId), contractInstance)
this.getHash(what, networkId),
contractInstance
)
} }
protected static hasContract(what: string, networkId: number): boolean { protected static hasContract(what: string, networkId: number): boolean {
return ContractHandler.contracts.has(this.getHash(what, networkId)) return ContractHandler.contracts.has(this.getHash(what, networkId))
} }
private static contracts: Map<string, Contract> = new Map< private static contracts: Map<string, Contract> = new Map<string, Contract>()
string,
Contract
>()
private static getHash(what: string, networkId: number): string { private static getHash(what: string, networkId: number): string {
return `${what}/#${networkId}` return `${what}/#${networkId}`
@ -35,10 +29,7 @@ export default class ContractHandler extends Instantiable {
this.setInstanceConfig(config) this.setInstanceConfig(config)
} }
public async get( public async get(what: string, optional: boolean = false): Promise<Contract> {
what: string,
optional: boolean = false
): Promise<Contract> {
const where = (await this.ocean.keeper.getNetworkName()).toLowerCase() const where = (await this.ocean.keeper.getNetworkName()).toLowerCase()
const networkId = await this.ocean.keeper.getNetworkId() const networkId = await this.ocean.keeper.getNetworkId()
try { try {
@ -65,14 +56,9 @@ export default class ContractHandler extends Instantiable {
const code = await this.web3.eth.getCode(artifact.address) const code = await this.web3.eth.getCode(artifact.address)
if (code === '0x0') { if (code === '0x0') {
// no code in the blockchain dude // no code in the blockchain dude
throw new Error( throw new Error(`No code deployed at address ${artifact.address}, sorry.`)
`No code deployed at address ${artifact.address}, sorry.`
)
} }
const contract = new this.web3.eth.Contract( const contract = new this.web3.eth.Contract(artifact.abi, artifact.address)
artifact.abi,
artifact.address
)
this.logger.debug( this.logger.debug(
'Getting instance of', 'Getting instance of',

View File

@ -7,11 +7,13 @@ import {
Condition, Condition,
LockRewardCondition, LockRewardCondition,
EscrowReward, EscrowReward,
AccessSecretStoreCondition AccessSecretStoreCondition,
ComputeExecutionCondition
} from './contracts/conditions' } from './contracts/conditions'
import { import {
AgreementTemplate, AgreementTemplate,
EscrowAccessSecretStoreTemplate EscrowAccessSecretStoreTemplate,
EscrowComputeExecutionTemplate
} from './contracts/templates' } from './contracts/templates'
import { import {
TemplateStoreManager, TemplateStoreManager,
@ -36,9 +38,7 @@ export class Keeper extends Instantiable {
* Returns Keeper instance. * Returns Keeper instance.
* @return {Promise<Keeper>} * @return {Promise<Keeper>}
*/ */
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<Keeper> {
config: InstantiableConfig
): Promise<Keeper> {
const keeper = new Keeper() const keeper = new Keeper()
keeper.setInstanceConfig(config) keeper.setInstanceConfig(config)
@ -54,21 +54,21 @@ export class Keeper extends Instantiable {
didRegistry: DIDRegistry.getInstance(config), didRegistry: DIDRegistry.getInstance(config),
// Managers // Managers
templateStoreManager: TemplateStoreManager.getInstance(config), templateStoreManager: TemplateStoreManager.getInstance(config),
agreementStoreManager: AgreementStoreManager.getInstance( agreementStoreManager: AgreementStoreManager.getInstance(config),
config conditionStoreManager: ConditionStoreManager.getInstance(config),
),
conditionStoreManager: ConditionStoreManager.getInstance(
config
),
// Conditions // Conditions
lockRewardCondition: LockRewardCondition.getInstance(config), lockRewardCondition: LockRewardCondition.getInstance(config),
escrowReward: EscrowReward.getInstance(config), escrowReward: EscrowReward.getInstance(config),
accessSecretStoreCondition: AccessSecretStoreCondition.getInstance( accessSecretStoreCondition: AccessSecretStoreCondition.getInstance(
config config
), ),
computeExecutionCondition: ComputeExecutionCondition.getInstance(config),
// Templates // Templates
escrowAccessSecretStoreTemplate: EscrowAccessSecretStoreTemplate.getInstance( escrowAccessSecretStoreTemplate: EscrowAccessSecretStoreTemplate.getInstance(
config config
),
escrowComputeExecutionTemplate: EscrowComputeExecutionTemplate.getInstance(
config
) )
}) })
@ -97,15 +97,16 @@ export class Keeper extends Instantiable {
keeper.conditions = { keeper.conditions = {
lockRewardCondition: keeper.instances.lockRewardCondition, lockRewardCondition: keeper.instances.lockRewardCondition,
escrowReward: keeper.instances.escrowReward, escrowReward: keeper.instances.escrowReward,
accessSecretStoreCondition: accessSecretStoreCondition: keeper.instances.accessSecretStoreCondition,
keeper.instances.accessSecretStoreCondition computeExecutionCondition: keeper.instances.computeExecutionCondition
} }
// Conditions // Conditions
keeper.templates = { keeper.templates = {
escrowAccessSecretStoreTemplate: escrowAccessSecretStoreTemplate:
keeper.instances.escrowAccessSecretStoreTemplate keeper.instances.escrowAccessSecretStoreTemplate,
escrowComputeExecutionTemplate:
keeper.instances.escrowComputeExecutionTemplate
} }
// Utils // Utils
keeper.utils = { keeper.utils = {
eventHandler: new EventHandler(config) eventHandler: new EventHandler(config)
@ -163,6 +164,7 @@ export class Keeper extends Instantiable {
lockRewardCondition: LockRewardCondition lockRewardCondition: LockRewardCondition
escrowReward: EscrowReward escrowReward: EscrowReward
accessSecretStoreCondition: AccessSecretStoreCondition accessSecretStoreCondition: AccessSecretStoreCondition
computeExecutionCondition: ComputeExecutionCondition
} }
/** /**
@ -170,6 +172,7 @@ export class Keeper extends Instantiable {
*/ */
public templates: { public templates: {
escrowAccessSecretStoreTemplate: EscrowAccessSecretStoreTemplate escrowAccessSecretStoreTemplate: EscrowAccessSecretStoreTemplate
escrowComputeExecutionTemplate: EscrowComputeExecutionTemplate
} }
/** /**
@ -227,7 +230,7 @@ export class Keeper extends Instantiable {
* @return {Promise<string>} Network name. * @return {Promise<string>} Network name.
*/ */
public getNetworkName(): Promise<string> { public getNetworkName(): Promise<string> {
return this.web3.eth.net.getId().then(networkId => { return this.web3.eth.net.getId().then((networkId: number) => {
switch (networkId) { switch (networkId) {
case 1: case 1:
return 'Main' return 'Main'

View File

@ -1,4 +1,4 @@
import * as Web3 from 'web3' import Web3 from 'web3'
import Config from '../models/Config' import Config from '../models/Config'
export default class Web3Provider { export default class Web3Provider {

View File

@ -15,7 +15,7 @@ export abstract class ContractBase extends Instantiable {
return this.contract.options.address return this.contract.options.address
} }
constructor(contractName, private optional: boolean = false) { constructor(contractName: string, private optional: boolean = false) {
super() super()
this.contractName = contractName this.contractName = contractName
} }
@ -54,10 +54,7 @@ export abstract class ContractBase extends Instantiable {
protected async init(config: InstantiableConfig) { protected async init(config: InstantiableConfig) {
this.setInstanceConfig(config) this.setInstanceConfig(config)
const contractHandler = new ContractHandler(config) const contractHandler = new ContractHandler(config)
this.contract = await contractHandler.get( this.contract = await contractHandler.get(this.contractName, this.optional)
this.contractName,
this.optional
)
} }
protected async getFromAddress(from?: string): Promise<string> { protected async getFromAddress(from?: string): Promise<string> {
@ -99,23 +96,19 @@ export abstract class ContractBase extends Instantiable {
}) })
return tx return tx
} catch (err) { } catch (err) {
const mappedArgs = this.searchMethod(name, args).inputs.map( const mappedArgs = this.searchMethod(name, args).inputs.map((input, i) => {
(input, i) => { return {
return { name: input.name,
name: input.name, value: args[i]
value: args[i]
}
} }
) })
this.logger.error('-'.repeat(40)) this.logger.error('-'.repeat(40))
this.logger.error( this.logger.error(
`Sending transaction "${name}" on contract "${this.contractName}" failed.` `Sending transaction "${name}" on contract "${this.contractName}" failed.`
) )
this.logger.error(`Error: ${err.message}`) this.logger.error(`Error: ${err.message}`)
this.logger.error(`From: ${from}`) this.logger.error(`From: ${from}`)
this.logger.error( this.logger.error(`Parameters: ${JSON.stringify(mappedArgs, null, 2)}`)
`Parameters: ${JSON.stringify(mappedArgs, null, 2)}`
)
this.logger.error('-'.repeat(40)) this.logger.error('-'.repeat(40))
throw err throw err
} }
@ -127,9 +120,7 @@ export abstract class ContractBase extends Instantiable {
from?: string from?: string
): Promise<T> { ): Promise<T> {
if (!this.contract.methods[name]) { if (!this.contract.methods[name]) {
throw new Error( throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
`Method ${name} is not part of contract ${this.contractName}`
)
} }
// Logger.log(name) // Logger.log(name)
try { try {
@ -150,11 +141,7 @@ export abstract class ContractBase extends Instantiable {
`Event ${eventName} is not part of contract ${this.contractName}` `Event ${eventName} is not part of contract ${this.contractName}`
) )
} }
return this.ocean.keeper.utils.eventHandler.getEvent( return this.ocean.keeper.utils.eventHandler.getEvent(this, eventName, filter)
this,
eventName,
filter
)
} }
private searchMethod(methodName: string, args: any[] = []) { private searchMethod(methodName: string, args: any[] = []) {
@ -165,8 +152,7 @@ export abstract class ContractBase extends Instantiable {
})) }))
.filter((method: any) => method.name === methodName) .filter((method: any) => method.name === methodName)
const foundMethod = const foundMethod =
methods.find(({ inputs }) => inputs.length === args.length) || methods.find(({ inputs }) => inputs.length === args.length) || methods[0]
methods[0]
if (!foundMethod) { if (!foundMethod) {
throw new Error( throw new Error(
`Method "${methodName}" is not part of contract "${this.contractName}"` `Method "${methodName}" is not part of contract "${this.contractName}"`

View File

@ -1,11 +1,10 @@
import { TransactionReceipt } from 'web3-core'
import ContractBase from './ContractBase' import ContractBase from './ContractBase'
import { zeroX, didPrefixed, didZeroX } from '../../utils' import { zeroX, noZeroX, didPrefixed, didZeroX } from '../../utils'
import { InstantiableConfig } from '../../Instantiable.abstract' import { InstantiableConfig } from '../../Instantiable.abstract'
export default class DIDRegistry extends ContractBase { export default class DIDRegistry extends ContractBase {
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<DIDRegistry> {
config: InstantiableConfig
): Promise<DIDRegistry> {
const didRegistry: DIDRegistry = new DIDRegistry('DIDRegistry') const didRegistry: DIDRegistry = new DIDRegistry('DIDRegistry')
await didRegistry.init(config) await didRegistry.init(config)
return didRegistry return didRegistry
@ -39,9 +38,11 @@ export default class DIDRegistry extends ContractBase {
} }
public async getAttributesByOwner(owner: string): Promise<string[]> { public async getAttributesByOwner(owner: string): Promise<string[]> {
return (await this.getPastEvents('DIDAttributeRegistered', { return (
_owner: zeroX(owner) await this.getPastEvents('DIDAttributeRegistered', {
})) _owner: zeroX(owner)
})
)
.map(({ returnValues }) => returnValues._did) .map(({ returnValues }) => returnValues._did)
.map(didPrefixed) .map(didPrefixed)
} }
@ -49,15 +50,13 @@ export default class DIDRegistry extends ContractBase {
public async getAttributesByDid( public async getAttributesByDid(
did: string did: string
): Promise<{ did: string; serviceEndpoint: string; checksum: string }> { ): Promise<{ did: string; serviceEndpoint: string; checksum: string }> {
return (await this.getPastEvents('DIDAttributeRegistered', { return (
_did: didZeroX(did) await this.getPastEvents('DIDAttributeRegistered', {
})).map( _did: didZeroX(did)
})
).map(
({ ({
returnValues: { returnValues: { _did, _checksum: checksum, _value: serviceEndpoint }
_did,
_checksum: checksum,
_value: serviceEndpoint
}
}) => ({ }) => ({
did: didPrefixed(_did), did: didPrefixed(_did),
serviceEndpoint, serviceEndpoint,
@ -65,4 +64,27 @@ export default class DIDRegistry extends ContractBase {
}) })
)[0] )[0]
} }
public async grantPermission(did: string, grantee: string, ownerAddress: string) {
return this.send('grantPermission', ownerAddress, [didZeroX(did), zeroX(grantee)])
}
public async revokePermission(did: string, grantee: string, ownerAddress: string) {
return this.send('revokePermission', ownerAddress, [zeroX(did), zeroX(grantee)])
}
public async getPermission(did: string, grantee: string): Promise<boolean> {
return this.call('getPermission', [didZeroX(did), zeroX(grantee)])
}
public async transferDIDOwnership(
did: string,
newOwnerAddress: string,
ownerAddress: string
): Promise<TransactionReceipt> {
return this.send('transferDIDOwnership', ownerAddress, [
didZeroX(did),
noZeroX(newOwnerAddress)
])
}
} }

View File

@ -2,18 +2,13 @@ import ContractBase from './ContractBase'
import { InstantiableConfig } from '../../Instantiable.abstract' import { InstantiableConfig } from '../../Instantiable.abstract'
export default class Dispenser extends ContractBase { export default class Dispenser extends ContractBase {
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<Dispenser> {
config: InstantiableConfig
): Promise<Dispenser> {
const dispenser: Dispenser = new Dispenser('Dispenser', true) const dispenser: Dispenser = new Dispenser('Dispenser', true)
await dispenser.init(config) await dispenser.init(config)
return dispenser return dispenser
} }
public async requestTokens( public async requestTokens(amount: number | string, receiverAddress: string) {
amount: number | string,
receiverAddress: string
) {
return this.send('requestTokens', receiverAddress, [String(amount)]) return this.send('requestTokens', receiverAddress, [String(amount)])
} }
} }

View File

@ -3,9 +3,7 @@ import ContractBase from './ContractBase'
import { InstantiableConfig } from '../../Instantiable.abstract' import { InstantiableConfig } from '../../Instantiable.abstract'
export default class OceanToken extends ContractBase { export default class OceanToken extends ContractBase {
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<OceanToken> {
config: InstantiableConfig
): Promise<OceanToken> {
const token: OceanToken = new OceanToken('OceanToken') const token: OceanToken = new OceanToken('OceanToken')
await token.init(config) await token.init(config)
return token return token

View File

@ -17,17 +17,8 @@ export class AccessSecretStoreCondition extends Condition {
return super.hashValues(didZeroX(did), zeroX(grantee)) return super.hashValues(didZeroX(did), zeroX(grantee))
} }
public fulfill( public fulfill(agreementId: string, did: string, grantee: string, from?: string) {
agreementId: string, return super.fulfill(agreementId, [didZeroX(did), grantee].map(zeroX), from)
did: string,
grantee: string,
from?: string
) {
return super.fulfill(
agreementId,
[didZeroX(did), grantee].map(zeroX),
from
)
} }
public checkPermissions(grantee: string, did: string, from?: string) { public checkPermissions(grantee: string, did: string, from?: string) {
@ -41,9 +32,11 @@ export class AccessSecretStoreCondition extends Condition {
public async getGrantedDidByConsumer( public async getGrantedDidByConsumer(
consumer: string consumer: string
): Promise<{ did: string; agreementId: string }[]> { ): Promise<{ did: string; agreementId: string }[]> {
return (await this.getPastEvents('Fulfilled', { return (
_grantee: zeroX(consumer) await this.getPastEvents('Fulfilled', {
})).map(({ returnValues }) => ({ _grantee: zeroX(consumer)
})
).map(({ returnValues }) => ({
did: didPrefixed(returnValues._documentId), did: didPrefixed(returnValues._documentId),
agreementId: zeroX(returnValues._agreementId) agreementId: zeroX(returnValues._agreementId)
})) }))

View File

@ -0,0 +1,40 @@
import { Condition } from './Condition.abstract'
import { zeroX, didZeroX, didPrefixed } from '../../../utils'
import { InstantiableConfig } from '../../../Instantiable.abstract'
export class ComputeExecutionCondition extends Condition {
public static async getInstance(
config: InstantiableConfig
): Promise<ComputeExecutionCondition> {
return Condition.getInstance(
config,
'ComputeExecutionCondition',
ComputeExecutionCondition
)
}
public hashValues(did: string, computeConsumer: string) {
return super.hashValues(didZeroX(did), zeroX(computeConsumer))
}
public fulfill(
agreementId: string,
did: string,
computeConsumer: string,
from?: string
) {
return super.fulfill(
agreementId,
[didZeroX(did), computeConsumer].map(zeroX),
from
)
}
public wasComputeTriggered(did: string, computeConsumer: string, from?: string) {
return this.call<boolean>(
'wasComputeTriggered',
[didZeroX(did), computeConsumer].map(zeroX),
from
)
}
}

View File

@ -3,9 +3,7 @@ import { zeroX } from '../../../utils'
import { InstantiableConfig } from '../../../Instantiable.abstract' import { InstantiableConfig } from '../../../Instantiable.abstract'
export class EscrowReward extends Condition { export class EscrowReward extends Condition {
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<EscrowReward> {
config: InstantiableConfig
): Promise<EscrowReward> {
return Condition.getInstance(config, 'EscrowReward', EscrowReward) return Condition.getInstance(config, 'EscrowReward', EscrowReward)
} }
@ -33,12 +31,7 @@ export class EscrowReward extends Condition {
) { ) {
return super.fulfill( return super.fulfill(
agreementId, agreementId,
[ [amount, ...[receiver, sender, lockCondition, releaseCondition].map(zeroX)],
amount,
...[receiver, sender, lockCondition, releaseCondition].map(
zeroX
)
],
from from
) )
} }

View File

@ -6,11 +6,7 @@ export class LockRewardCondition extends Condition {
public static async getInstance( public static async getInstance(
config: InstantiableConfig config: InstantiableConfig
): Promise<LockRewardCondition> { ): Promise<LockRewardCondition> {
return Condition.getInstance( return Condition.getInstance(config, 'LockRewardCondition', LockRewardCondition)
config,
'LockRewardCondition',
LockRewardCondition
)
} }
public hashValues(rewardAddress: string, amount: number | string) { public hashValues(rewardAddress: string, amount: number | string) {
@ -23,10 +19,6 @@ export class LockRewardCondition extends Condition {
amount: number | string, amount: number | string,
from?: string from?: string
) { ) {
return super.fulfill( return super.fulfill(agreementId, [zeroX(rewardAddress), String(amount)], from)
agreementId,
[zeroX(rewardAddress), String(amount)],
from
)
} }
} }

View File

@ -2,3 +2,4 @@ export * from './Condition.abstract'
export { AccessSecretStoreCondition } from './AccessSecretStoreCondition' export { AccessSecretStoreCondition } from './AccessSecretStoreCondition'
export { EscrowReward } from './EscrowReward' export { EscrowReward } from './EscrowReward'
export { LockRewardCondition } from './LockRewardCondition' export { LockRewardCondition } from './LockRewardCondition'
export { ComputeExecutionCondition } from './ComputeExecutionCondition'

View File

@ -31,11 +31,7 @@ export class TemplateStoreManager extends ContractBase {
return this.call('owner', []) return this.call('owner', [])
} }
public async proposeTemplate( public async proposeTemplate(address: string, from?: string, ignoreExists?: boolean) {
address: string,
from?: string,
ignoreExists?: boolean
) {
const template = await this.getTemplate(address) const template = await this.getTemplate(address)
if (template.blockNumberUpdated !== 0) { if (template.blockNumberUpdated !== 0) {
this.logger.warn(`Template "${address}" already exist.`) this.logger.warn(`Template "${address}" already exist.`)
@ -54,9 +50,7 @@ export class TemplateStoreManager extends ContractBase {
) { ) {
const template = await this.getTemplate(address) const template = await this.getTemplate(address)
if (template.state !== TemplateState.Proposed) { if (template.state !== TemplateState.Proposed) {
this.logger.warn( this.logger.warn(`Template "${address}" is not in "proposed" state.`)
`Template "${address}" is not in "proposed" state.`
)
if (!ignoreApproved) { if (!ignoreApproved) {
throw new Error(`Template not in "proposed" state.`) throw new Error(`Template not in "proposed" state.`)
} }

View File

@ -25,9 +25,7 @@ export abstract class AgreementTemplate extends ContractBase {
conditionName: string, conditionName: string,
templateClass: any templateClass: any
): Promise<AgreementTemplate & any> { ): Promise<AgreementTemplate & any> {
const condition: AgreementTemplate = new (templateClass as any)( const condition: AgreementTemplate = new (templateClass as any)(conditionName)
conditionName
)
await condition.init(config) await condition.init(config)
return condition return condition
} }
@ -114,9 +112,7 @@ export abstract class AgreementTemplate extends ContractBase {
from?: string from?: string
): Promise<boolean> ): Promise<boolean>
public abstract async getServiceAgreementTemplate(): Promise< public abstract async getServiceAgreementTemplate(): Promise<ServiceAgreementTemplate>
ServiceAgreementTemplate
>
public async getServiceAgreementTemplateConditions() { public async getServiceAgreementTemplateConditions() {
const serviceAgreementTemplate = await this.getServiceAgreementTemplate() const serviceAgreementTemplate = await this.getServiceAgreementTemplate()
@ -165,15 +161,17 @@ export abstract class AgreementTemplate extends ContractBase {
) )
const statesPromises = Object.keys(dependencies).map(async (ref, i) => { const statesPromises = Object.keys(dependencies).map(async (ref, i) => {
const { const { contractName } = await this.getServiceAgreementTemplateConditionByRef(
contractName ref
} = await this.getServiceAgreementTemplateConditionByRef(ref) )
return { return {
ref, ref,
contractName, contractName,
state: (await conditionStore.getCondition( state: (
conditionIdByConddition[contractName] await conditionStore.getCondition(
)).state conditionIdByConddition[contractName]
)
).state
} }
}) })
const states = await Promise.all(statesPromises) const states = await Promise.all(statesPromises)
@ -181,9 +179,7 @@ export abstract class AgreementTemplate extends ContractBase {
return states.reduce((acc, { contractName, ref, state }) => { return states.reduce((acc, { contractName, ref, state }) => {
const blockers = dependencies[ref] const blockers = dependencies[ref]
.map(dependency => states.find(_ => _.ref === dependency)) .map(dependency => states.find(_ => _.ref === dependency))
.filter( .filter(condition => condition.state !== ConditionState.Fulfilled)
condition => condition.state !== ConditionState.Fulfilled
)
return { return {
...acc, ...acc,
[ref]: { [ref]: {
@ -217,11 +213,7 @@ export abstract class AgreementTemplate extends ContractBase {
this.logger.bypass('-'.repeat(20)) this.logger.bypass('-'.repeat(20))
} }
this.logger.bypass(`${condition} (${contractName})`) this.logger.bypass(`${condition} (${contractName})`)
this.logger.bypass( this.logger.bypass(' Status:', state, `(${conditionStateNames[state]})`)
' Status:',
state,
`(${conditionStateNames[state]})`
)
if (blocked) { if (blocked) {
this.logger.bypass(' Blocked by:', blockedBy) this.logger.bypass(' Blocked by:', blockedBy)
} }

View File

@ -0,0 +1,43 @@
import { AgreementTemplate } from './AgreementTemplate.abstract'
import { DDO } from '../../../ddo/DDO'
import { generateId, zeroX } from '../../../utils'
import { InstantiableConfig } from '../../../Instantiable.abstract'
// import { EscrowComputeExecutionTemplateServiceAgreementTemplate } from './EscrowComputeExecutionTemplate.serviceAgreementTemplate'
export abstract class BaseEscrowTemplate extends AgreementTemplate {
/**
* Create a agreement using EscrowComputeExecutionTemplate.
* @param {string} agreementId Generated agreement ID.
* @param {string} did Asset DID.
* @param {string[]} conditionIds List of conditions IDs.
* @param {number[]} timeLocks Timelocks.
* @param {number[]} timeOuts Timeouts.
* @param {string} accessConsumer Consumer address.
* @param {string} from Action sender.
* @param {any} Transaction receipt.
*/
public createAgreement(
agreementId: string,
did: string,
conditionIds: string[],
timeLocks: number[],
timeOuts: number[],
accessConsumer: string,
from?: string
) {
return super.createAgreement(
agreementId,
did,
conditionIds,
timeLocks,
timeOuts,
[accessConsumer],
from
)
}
public async getAgreementData(agreementId: string) {
return this.call<any>('getAgreementData', [zeroX(agreementId)])
}
}

View File

@ -1,11 +1,12 @@
import { AgreementTemplate } from './AgreementTemplate.abstract' import { AgreementTemplate } from './AgreementTemplate.abstract'
import { BaseEscrowTemplate } from './BaseEscrowTemplate.abstract'
import { DDO } from '../../../ddo/DDO' import { DDO } from '../../../ddo/DDO'
import { generateId, zeroX } from '../../../utils' import { generateId, zeroX } from '../../../utils'
import { InstantiableConfig } from '../../../Instantiable.abstract' import { InstantiableConfig } from '../../../Instantiable.abstract'
import { escrowAccessSecretStoreTemplateServiceAgreementTemplate } from './EscrowAccessSecretStoreTemplate.serviceAgreementTemplate' import { escrowAccessSecretStoreTemplateServiceAgreementTemplate } from './EscrowAccessSecretStoreTemplate.serviceAgreementTemplate'
export class EscrowAccessSecretStoreTemplate extends AgreementTemplate { export class EscrowAccessSecretStoreTemplate extends BaseEscrowTemplate {
public static async getInstance( public static async getInstance(
config: InstantiableConfig config: InstantiableConfig
): Promise<EscrowAccessSecretStoreTemplate> { ): Promise<EscrowAccessSecretStoreTemplate> {
@ -20,37 +21,6 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
return escrowAccessSecretStoreTemplateServiceAgreementTemplate return escrowAccessSecretStoreTemplateServiceAgreementTemplate
} }
/**
* Create a agreement using EscrowAccessSecretStoreTemplate.
* @param {string} agreementId Generated agreement ID.
* @param {string} did Asset DID.
* @param {string[]} conditionIds List of conditions IDs.
* @param {number[]} timeLocks Timelocks.
* @param {number[]} timeOuts Timeouts.
* @param {string} accessConsumer Consumer address.
* @param {string} from Action sender.
* @param {any} Transaction receipt.
*/
public createAgreement(
agreementId: string,
did: string,
conditionIds: string[],
timeLocks: number[],
timeOuts: number[],
accessConsumer: string,
from?: string
) {
return super.createAgreement(
agreementId,
did,
conditionIds,
timeLocks,
timeOuts,
[accessConsumer],
from
)
}
public async createAgreementFromDDO( public async createAgreementFromDDO(
agreementId: string, agreementId: string,
ddo: DDO, ddo: DDO,
@ -59,7 +29,7 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
) { ) {
return !!(await this.createFullAgreement( return !!(await this.createFullAgreement(
ddo.shortId(), ddo.shortId(),
ddo.findServiceByType('Metadata').metadata.base.price, ddo.findServiceByType('metadata').attributes.main.price,
consumer, consumer,
from, from,
agreementId agreementId
@ -79,14 +49,10 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
} = await this.createFullAgreementData( } = await this.createFullAgreementData(
agreementId, agreementId,
ddo.shortId(), ddo.shortId(),
ddo.findServiceByType('Metadata').metadata.base.price, ddo.findServiceByType('metadata').attributes.main.price,
consumer consumer
) )
return [ return [accessSecretStoreConditionId, lockRewardConditionId, escrowRewardId]
accessSecretStoreConditionId,
lockRewardConditionId,
escrowRewardId
]
} }
/** /**
@ -107,21 +73,12 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
accessSecretStoreConditionId, accessSecretStoreConditionId,
lockRewardConditionId, lockRewardConditionId,
escrowRewardId escrowRewardId
} = await this.createFullAgreementData( } = await this.createFullAgreementData(agreementId, did, amount, consumer)
agreementId,
did,
amount,
consumer
)
await this.createAgreement( await this.createAgreement(
agreementId, agreementId,
did, did,
[ [accessSecretStoreConditionId, lockRewardConditionId, escrowRewardId],
accessSecretStoreConditionId,
lockRewardConditionId,
escrowRewardId
],
[0, 0, 0], [0, 0, 0],
[0, 0, 0], [0, 0, 0],
consumer, consumer,
@ -172,8 +129,4 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
escrowRewardId escrowRewardId
} }
} }
public async getAgreementData(agreementId: string) {
return this.call<any>('getAgreementData', [zeroX(agreementId)])
}
} }

View File

@ -0,0 +1,142 @@
import { ServiceAgreementTemplate } from '../../../ddo/ServiceAgreementTemplate'
export const escrowComputeExecutionTemplateServiceAgreementTemplate: 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: [],
escrowReward: ['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'
}
}
]
}
]
}

View File

@ -0,0 +1,132 @@
import { AgreementTemplate } from './AgreementTemplate.abstract'
import { BaseEscrowTemplate } from './BaseEscrowTemplate.abstract'
import { DDO } from '../../../ddo/DDO'
import { generateId, zeroX } from '../../../utils'
import { InstantiableConfig } from '../../../Instantiable.abstract'
import { escrowComputeExecutionTemplateServiceAgreementTemplate } from './EscrowComputeExecutionTemplate.serviceAgreementTemplate'
export class EscrowComputeExecutionTemplate extends BaseEscrowTemplate {
public static async getInstance(
config: InstantiableConfig
): Promise<EscrowComputeExecutionTemplate> {
return AgreementTemplate.getInstance(
config,
'EscrowComputeExecutionTemplate',
EscrowComputeExecutionTemplate
)
}
public async getServiceAgreementTemplate() {
return escrowComputeExecutionTemplateServiceAgreementTemplate
}
public async createAgreementFromDDO(
agreementId: string,
ddo: DDO,
consumer: string,
from?: string
) {
return !!(await this.createFullAgreement(
ddo.shortId(),
ddo.findServiceByType('metadata').attributes.main.price,
consumer,
from,
agreementId
))
}
public async getAgreementIdsFromDDO(
agreementId: string,
ddo: DDO,
consumer: string,
from?: string
) {
const {
computeExecutionConditionId,
lockRewardConditionId,
escrowRewardId
} = await this.createFullAgreementData(
agreementId,
ddo.shortId(),
ddo.findServiceByType('metadata').attributes.main.price,
consumer
)
return [computeExecutionConditionId, lockRewardConditionId, escrowRewardId]
}
/**
* Create a agreement using EscrowAccess____SecretStoreTemplate using only the most important information.
* @param {string} did Asset DID.
* @param {number} amount Asset price.
* @param {string} from Consumer address.
* @return {Promise<string>} Agreement ID.
*/
public async createFullAgreement(
did: string,
amount: number | string,
consumer: string,
from?: string,
agreementId: string = generateId()
): Promise<string> {
const {
computeExecutionConditionId,
lockRewardConditionId,
escrowRewardId
} = await this.createFullAgreementData(agreementId, did, amount, consumer)
await this.createAgreement(
agreementId,
did,
[computeExecutionConditionId, lockRewardConditionId, escrowRewardId],
[0, 0, 0],
[0, 0, 0],
consumer,
from
)
return zeroX(agreementId)
}
private async createFullAgreementData(
agreementId: string,
did: string,
amount: number | string,
consumer: string
) {
const { didRegistry, conditions } = this.ocean.keeper
const {
computeExecutionCondition,
lockRewardCondition,
escrowReward
} = conditions
const publisher = await didRegistry.getDIDOwner(did)
const lockRewardConditionId = await lockRewardCondition.generateIdHash(
agreementId,
await escrowReward.getAddress(),
amount
)
const computeExecutionConditionId = await computeExecutionCondition.generateIdHash(
agreementId,
did,
consumer
)
const escrowRewardId = await escrowReward.generateIdHash(
agreementId,
String(amount),
publisher,
consumer,
lockRewardConditionId,
computeExecutionConditionId
)
return {
lockRewardConditionId,
computeExecutionConditionId,
escrowRewardId
}
}
}

View File

@ -1,4 +1,4 @@
export * from './AgreementTemplate.abstract' export * from './AgreementTemplate.abstract'
export { export { BaseEscrowTemplate } from './BaseEscrowTemplate.abstract'
EscrowAccessSecretStoreTemplate export { EscrowAccessSecretStoreTemplate } from './EscrowAccessSecretStoreTemplate'
} from './EscrowAccessSecretStoreTemplate' export { EscrowComputeExecutionTemplate } from './EscrowComputeExecutionTemplate'

View File

@ -46,9 +46,7 @@ export class Ocean extends Instantiable {
instance.auth = await OceanAuth.getInstance(instanceConfig) instance.auth = await OceanAuth.getInstance(instanceConfig)
instance.assets = await OceanAssets.getInstance(instanceConfig) instance.assets = await OceanAssets.getInstance(instanceConfig)
instance.agreements = await OceanAgreements.getInstance(instanceConfig) instance.agreements = await OceanAgreements.getInstance(instanceConfig)
instance.secretStore = await OceanSecretStore.getInstance( instance.secretStore = await OceanSecretStore.getInstance(instanceConfig)
instanceConfig
)
instance.tokens = await OceanTokens.getInstance(instanceConfig) instance.tokens = await OceanTokens.getInstance(instanceConfig)
instance.versions = await OceanVersions.getInstance(instanceConfig) instance.versions = await OceanVersions.getInstance(instanceConfig)

View File

@ -10,9 +10,7 @@ export class OceanAccounts extends Instantiable {
* Returns the instance of OceanAccounts. * Returns the instance of OceanAccounts.
* @return {Promise<OceanAccounts>} * @return {Promise<OceanAccounts>}
*/ */
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<OceanAccounts> {
config: InstantiableConfig
): Promise<OceanAccounts> {
const instance = new OceanAccounts() const instance = new OceanAccounts()
instance.setInstanceConfig(config) instance.setInstanceConfig(config)
@ -43,15 +41,12 @@ export class OceanAccounts extends Instantiable {
} }
/** /**
* Request tokens for a account. * Request tokens for an account.
* @param {Account} account Account instance. * @param {Account} account Account instance.
* @param {number} amount Token amount. * @param {number} amount Token amount.
* @return {Promise<boolean>} Success. * @return {Promise<boolean>} Success.
*/ */
public async requestTokens( public async requestTokens(account: Account, amount: number): Promise<boolean> {
account: Account,
amount: number
): Promise<boolean> {
try { try {
await account.requestTokens(amount) await account.requestTokens(amount)
return true return true

View File

@ -26,9 +26,7 @@ export class OceanAgreements extends Instantiable {
): Promise<OceanAgreements> { ): Promise<OceanAgreements> {
const instance = new OceanAgreements() const instance = new OceanAgreements()
instance.setInstanceConfig(config) instance.setInstanceConfig(config)
instance.conditions = await OceanAgreementsConditions.getInstance( instance.conditions = await OceanAgreementsConditions.getInstance(config)
config
)
return instance return instance
} }
@ -42,33 +40,28 @@ export class OceanAgreements extends Instantiable {
/** /**
* Creates a consumer signature for the specified asset service. * Creates a consumer signature for the specified asset service.
* @param {string} did Decentralized ID. * @param {string} did Decentralized ID.
* @param {string} serviceDefinitionId Service definition ID. * @param {number} index Service index.
* @param {Account} consumer Consumer account. * @param {Account} consumer Consumer account.
* @return {Promise<AgreementPrepareResult>} Agreement ID and signaturee. * @return {Promise<AgreementPrepareResult>} Agreement ID and signaturee.
*/ */
public async prepare( public async prepare(
did: string, did: string,
serviceDefinitionId: string, index: number,
consumer: Account consumer: Account
): Promise<AgreementPrepareResult> { ): Promise<AgreementPrepareResult> {
const d: DID = DID.parse(did as string) const d: DID = DID.parse(did as string)
const ddo = await this.ocean.aquarius.retrieveDDO(d) const ddo = await this.ocean.aquarius.retrieveDDO(d)
const agreementId: string = zeroX(generateId()) const agreementId: string = zeroX(generateId())
const templateName = ddo.findServiceByType('Access') const templateName = ddo.findServiceByType('access').attributes
.serviceAgreementTemplate.contractName .serviceAgreementTemplate.contractName
const agreementConditionsIds = await this.ocean.keeper const agreementConditionsIds = await this.ocean.keeper
.getTemplateByName(templateName) .getTemplateByName(templateName)
.getAgreementIdsFromDDO( .getAgreementIdsFromDDO(agreementId, ddo, consumer.getId(), consumer.getId())
agreementId,
ddo,
consumer.getId(),
consumer.getId()
)
const signature = await this.ocean.utils.agreements.signServiceAgreement( const signature = await this.ocean.utils.agreements.signServiceAgreement(
ddo, ddo,
serviceDefinitionId, index,
agreementId, agreementId,
agreementConditionsIds, agreementConditionsIds,
consumer consumer
@ -80,29 +73,27 @@ export class OceanAgreements extends Instantiable {
/** /**
* Submit a service agreement to the publisher to create the agreement on-chain. * Submit a service agreement to the publisher to create the agreement on-chain.
* @param {string} did Decentralized ID. * @param {string} did Decentralized ID.
* @param {string} serviceDefinitionId Service definition ID. * @param {number} index Service index.
* @param {Account} consumer Consumer account. * @param {Account} consumer Consumer account.
* @return {Promise<void>} * @return {Promise<void>}
*/ */
public async send( public async send(
did: string, did: string,
agreementId: string, agreementId: string,
serviceDefinitionId: string, index: number,
signature: string, signature: string,
consumer: Account consumer: Account
): Promise<void> { ): Promise<void> {
const result = await this.ocean.brizo.initializeServiceAgreement( const result = await this.ocean.brizo.initializeServiceAgreement(
didPrefixed(did), didPrefixed(did),
zeroX(agreementId), zeroX(agreementId),
serviceDefinitionId, index,
zeroX(signature), zeroX(signature),
consumer.getId() consumer.getId()
) )
if (!result.ok) { if (!result.ok) {
throw new Error( throw new Error('Error on initialize agreement: ' + (await result.text()))
'Error on initialize agreement: ' + (await result.text())
)
} }
} }
@ -112,7 +103,7 @@ export class OceanAgreements extends Instantiable {
* in this method before submitting on-chain. * in this method before submitting on-chain.
* @param {string} did Decentralized ID. * @param {string} did Decentralized ID.
* @param {string} agreementId Service agreement ID. * @param {string} agreementId Service agreement ID.
* @param {string} serviceDefinitionId Service definition ID. * @param {number} index Service index.
* @param {string} signature Service agreement signature. * @param {string} signature Service agreement signature.
* @param {Account} consumer Consumer account. * @param {Account} consumer Consumer account.
* @param {Account} publisher Publisher account. * @param {Account} publisher Publisher account.
@ -121,7 +112,7 @@ export class OceanAgreements extends Instantiable {
public async create( public async create(
did: string, did: string,
agreementId: string, agreementId: string,
serviceDefinitionId: string, index: number,
signature: string, signature: string,
consumer: Account, consumer: Account,
publisher: Account publisher: Account
@ -129,25 +120,18 @@ export class OceanAgreements extends Instantiable {
const d: DID = DID.parse(did) const d: DID = DID.parse(did)
const ddo = await this.ocean.aquarius.retrieveDDO(d) const ddo = await this.ocean.aquarius.retrieveDDO(d)
const templateName = ddo.findServiceById<'Access'>(serviceDefinitionId) const templateName = ddo.findServiceById<'access'>(index).attributes
.serviceAgreementTemplate.contractName .serviceAgreementTemplate.contractName
await this.ocean.keeper await this.ocean.keeper
.getTemplateByName(templateName) .getTemplateByName(templateName)
.createAgreementFromDDO( .createAgreementFromDDO(agreementId, ddo, consumer.getId(), publisher.getId())
agreementId,
ddo,
consumer.getId(),
publisher.getId()
)
return true return true
} }
/** /**
* Get the status of a service agreement. * Get the status of a service agreement.
* @param {string} did Decentralized ID.
* @param {string} agreementId Service agreement ID. * @param {string} agreementId Service agreement ID.
* @param {string} serviceDefinitionId Service definition ID.
* @param {boolean} extended Returns a complete status with dependencies. * @param {boolean} extended Returns a complete status with dependencies.
* @return {Promise<any>} * @return {Promise<any>}
*/ */
@ -162,9 +146,7 @@ export class OceanAgreements extends Instantiable {
): Promise<AgreementConditionsStatus> ): Promise<AgreementConditionsStatus>
public async status(agreementId: string, extended: boolean = false) { public async status(agreementId: string, extended: boolean = false) {
const { const { templateId } = await this.ocean.keeper.agreementStoreManager.getAgreement(
templateId
} = await this.ocean.keeper.agreementStoreManager.getAgreement(
agreementId agreementId
) )
const fullStatus = await this.ocean.keeper const fullStatus = await this.ocean.keeper

View File

@ -30,10 +30,7 @@ export class OceanAgreementsConditions extends Instantiable {
amount: number | string, amount: number | string,
from?: Account from?: Account
) { ) {
const { const { lockRewardCondition, escrowReward } = this.ocean.keeper.conditions
lockRewardCondition,
escrowReward
} = this.ocean.keeper.conditions
try { try {
await this.ocean.keeper.token.approve( await this.ocean.keeper.token.approve(
@ -83,6 +80,34 @@ export class OceanAgreementsConditions extends Instantiable {
} }
} }
/**
* Authorize the consumer defined in the agreement to execute a remote service associated with this asset.
* @param {string} agreementId Agreement ID.
* @param {string} did Asset ID.
* @param {string} grantee Consumer address.
* @param {Account} from Account of sender.
*/
public async grantServiceExecution(
agreementId: string,
did: string,
grantee: string,
from?: Account
) {
try {
const { computeExecutionCondition } = this.ocean.keeper.conditions
const receipt = await computeExecutionCondition.fulfill(
agreementId,
did,
grantee,
from && from.getId()
)
return !!receipt.events.Fulfilled
} catch {
return false
}
}
/** /**
* Transfer the escrow or locked tokens from the LockRewardCondition contract to the publisher's account. * Transfer the escrow or locked tokens from the LockRewardCondition contract to the publisher's account.
* This should be allowed after access has been given to the consumer and the asset data is downloaded. * This should be allowed after access has been given to the consumer and the asset data is downloaded.

View File

@ -1,15 +1,11 @@
import { TransactionReceipt } from 'web3-core'
import { SearchQuery } from '../aquarius/Aquarius' import { SearchQuery } from '../aquarius/Aquarius'
import { DDO } from '../ddo/DDO' import { DDO } from '../ddo/DDO'
import { MetaData } from '../ddo/MetaData' import { MetaData } from '../ddo/MetaData'
import { Service } from '../ddo/Service' import { Service } from '../ddo/Service'
import Account from './Account' import Account from './Account'
import DID from './DID' import DID from './DID'
import { import { fillConditionsWithDDO, SubscribablePromise, generateId, zeroX } from '../utils'
fillConditionsWithDDO,
SubscribablePromise,
generateId,
zeroX
} from '../utils'
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract' import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
export enum CreateProgressStep { export enum CreateProgressStep {
@ -38,9 +34,7 @@ export class OceanAssets extends Instantiable {
* Returns the instance of OceanAssets. * Returns the instance of OceanAssets.
* @return {Promise<OceanAssets>} * @return {Promise<OceanAssets>}
*/ */
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<OceanAssets> {
config: InstantiableConfig
): Promise<OceanAssets> {
const instance = new OceanAssets() const instance = new OceanAssets()
instance.setInstanceConfig(config) instance.setInstanceConfig(config)
@ -81,7 +75,7 @@ export class OceanAssets extends Instantiable {
observer.next(CreateProgressStep.EncryptingFiles) observer.next(CreateProgressStep.EncryptingFiles)
const encryptedFiles = await this.ocean.secretStore.encrypt( const encryptedFiles = await this.ocean.secretStore.encrypt(
did.getId(), did.getId(),
metadata.base.files, metadata.main.files,
publisher publisher
) )
this.logger.log('Files encrypted') this.logger.log('Files encrypted')
@ -91,7 +85,7 @@ export class OceanAssets extends Instantiable {
const serviceEndpoint = this.ocean.aquarius.getServiceEndpoint(did) const serviceEndpoint = this.ocean.aquarius.getServiceEndpoint(did)
let serviceDefinitionIdCount = 0 let indexCount = 0
// create ddo itself // create ddo itself
const ddo: DDO = new DDO({ const ddo: DDO = new DDO({
id: did.getDid(), id: did.getDid(),
@ -110,23 +104,30 @@ export class OceanAssets extends Instantiable {
], ],
service: [ service: [
{ {
type: 'Access', type: 'access',
creator: '',
purchaseEndpoint: this.ocean.brizo.getPurchaseEndpoint(),
serviceEndpoint: this.ocean.brizo.getConsumeEndpoint(), serviceEndpoint: this.ocean.brizo.getConsumeEndpoint(),
name: 'dataAssetAccessServiceAgreement',
templateId: templates.escrowAccessSecretStoreTemplate.getAddress(), templateId: templates.escrowAccessSecretStoreTemplate.getAddress(),
serviceAgreementTemplate attributes: {
main: {
creator: publisher.getId(),
datePublished: metadata.main.datePublished,
name: 'dataAssetAccessServiceAgreement',
price: metadata.main.price,
timeout: 3600
},
serviceAgreementTemplate
}
}, },
{ {
type: 'Authorization', type: 'authorization',
service: 'SecretStore', service: 'SecretStore',
serviceEndpoint: secretStoreUri serviceEndpoint: secretStoreUri,
attributes: { main: {} }
}, },
{ {
type: 'Metadata', type: 'metadata',
serviceEndpoint, serviceEndpoint,
metadata: { attributes: {
// Default values // Default values
curation: { curation: {
rating: 0, rating: 0,
@ -134,18 +135,15 @@ export class OceanAssets extends Instantiable {
}, },
// Overwrites defaults // Overwrites defaults
...metadata, ...metadata,
encryptedFiles,
// Cleaning not needed information // Cleaning not needed information
base: { main: {
...metadata.base, ...metadata.main,
contentUrls: undefined, files: metadata.main.files.map((file, index) => ({
encryptedFiles, ...file,
files: metadata.base.files.map( index,
(file, index) => ({ url: undefined
...file, }))
index,
url: undefined
})
)
} as any } as any
} }
}, },
@ -158,26 +156,21 @@ export class OceanAssets extends Instantiable {
list.findIndex(({ type: t }) => t === type) === i list.findIndex(({ type: t }) => t === type) === i
) )
.reverse() .reverse()
// Adding ID // Adding index
.map(_ => ({ .map(_ => ({
..._, ..._,
serviceDefinitionId: String(serviceDefinitionIdCount++) index: indexCount++
})) as Service[] })) as Service[]
}) })
// Overwritte initial service agreement conditions // Overwrite initial service agreement conditions
const rawConditions = await templates.escrowAccessSecretStoreTemplate.getServiceAgreementTemplateConditions() const rawConditions = await templates.escrowAccessSecretStoreTemplate.getServiceAgreementTemplateConditions()
const conditions = fillConditionsWithDDO(rawConditions, ddo) const conditions = fillConditionsWithDDO(rawConditions, ddo)
serviceAgreementTemplate.conditions = conditions serviceAgreementTemplate.conditions = conditions
ddo.addChecksum()
this.logger.log('Generating proof') this.logger.log('Generating proof')
observer.next(CreateProgressStep.GeneratingProof) observer.next(CreateProgressStep.GeneratingProof)
await ddo.addProof( await ddo.addProof(this.ocean, publisher.getId(), publisher.getPassword())
this.ocean,
publisher.getId(),
publisher.getPassword()
)
this.logger.log('Proof generated') this.logger.log('Proof generated')
observer.next(CreateProgressStep.ProofGenerated) observer.next(CreateProgressStep.ProofGenerated)
@ -206,7 +199,7 @@ export class OceanAssets extends Instantiable {
public async consume( public async consume(
agreementId: string, agreementId: string,
did: string, did: string,
serviceDefinitionId: string, serviceIndex: number,
consumerAccount: Account, consumerAccount: Account,
resultPath: string, resultPath: string,
index?: number, index?: number,
@ -216,7 +209,7 @@ export class OceanAssets extends Instantiable {
public async consume( public async consume(
agreementId: string, agreementId: string,
did: string, did: string,
serviceDefinitionId: string, serviceIndex: number,
consumerAccount: Account, consumerAccount: Account,
resultPath?: undefined | null, resultPath?: undefined | null,
index?: number, index?: number,
@ -226,18 +219,18 @@ export class OceanAssets extends Instantiable {
public async consume( public async consume(
agreementId: string, agreementId: string,
did: string, did: string,
serviceDefinitionId: string, serviceIndex: number,
consumerAccount: Account, consumerAccount: Account,
resultPath?: string, resultPath?: string,
index: number = -1, index: number = -1,
useSecretStore?: boolean useSecretStore?: boolean
): Promise<string | true> { ): Promise<string | true> {
const ddo = await this.resolve(did) const ddo = await this.resolve(did)
const { metadata } = ddo.findServiceByType('Metadata') const { attributes } = ddo.findServiceByType('metadata')
const accessService = ddo.findServiceById(serviceDefinitionId) const accessService = ddo.findServiceById(serviceIndex)
const { files } = metadata.base const { files } = attributes.main
const { serviceEndpoint } = accessService const { serviceEndpoint } = accessService
@ -250,7 +243,7 @@ export class OceanAssets extends Instantiable {
this.logger.log('Consuming files') this.logger.log('Consuming files')
resultPath = resultPath resultPath = resultPath
? `${resultPath}/datafile.${ddo.shortId()}.${serviceDefinitionId}/` ? `${resultPath}/datafile.${ddo.shortId()}.${serviceIndex}/`
: undefined : undefined
if (!useSecretStore) { if (!useSecretStore) {
@ -265,9 +258,9 @@ export class OceanAssets extends Instantiable {
} else { } else {
const files = await this.ocean.secretStore.decrypt( const files = await this.ocean.secretStore.decrypt(
did, did,
ddo.findServiceByType('Metadata').metadata.base.encryptedFiles, ddo.findServiceByType('metadata').attributes.encryptedFiles,
consumerAccount, consumerAccount,
ddo.findServiceByType('Authorization').serviceEndpoint ddo.findServiceByType('authorization').serviceEndpoint
) )
const downloads = files const downloads = files
.filter(({ index: i }) => index === -1 || index === i) .filter(({ index: i }) => index === -1 || index === i)
@ -288,13 +281,13 @@ export class OceanAssets extends Instantiable {
* Start the purchase/order of an asset's service. Starts by signing the service agreement * 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). * then sends the request to the publisher via the service endpoint (Brizo http service).
* @param {string} did Decentralized ID. * @param {string} did Decentralized ID.
* @param {string} serviceDefinitionId Service definition ID. * @param {number} index Service index.
* @param {Account} consumer Consumer account. * @param {Account} consumer Consumer account.
* @return {Promise<string>} Returns Agreement ID * @return {Promise<string>} Returns Agreement ID
*/ */
public order( public order(
did: string, did: string,
serviceDefinitionId: string, index: number,
consumer: Account consumer: Account
): SubscribablePromise<OrderProgressStep, string> { ): SubscribablePromise<OrderProgressStep, string> {
return new SubscribablePromise(async observer => { return new SubscribablePromise(async observer => {
@ -304,7 +297,7 @@ export class OceanAssets extends Instantiable {
const ddo = await this.resolve(did) const ddo = await this.resolve(did)
const { keeper } = this.ocean const { keeper } = this.ocean
const templateName = ddo.findServiceByType('Access') const templateName = ddo.findServiceByType('access').attributes
.serviceAgreementTemplate.contractName .serviceAgreementTemplate.contractName
const template = keeper.getTemplateByName(templateName) const template = keeper.getTemplateByName(templateName)
const accessCondition = keeper.conditions.accessSecretStoreCondition const accessCondition = keeper.conditions.accessSecretStoreCondition
@ -316,7 +309,7 @@ export class OceanAssets extends Instantiable {
this.logger.log('Agreement initialized') this.logger.log('Agreement initialized')
observer.next(OrderProgressStep.AgreementInitialized) observer.next(OrderProgressStep.AgreementInitialized)
const { metadata } = ddo.findServiceByType('Metadata') const { attributes } = ddo.findServiceByType('metadata')
this.logger.log('Locking payment') this.logger.log('Locking payment')
@ -327,7 +320,7 @@ export class OceanAssets extends Instantiable {
observer.next(OrderProgressStep.LockingPayment) observer.next(OrderProgressStep.LockingPayment)
const paid = await oceanAgreements.conditions.lockReward( const paid = await oceanAgreements.conditions.lockReward(
agreementId, agreementId,
metadata.base.price, attributes.main.price,
consumer consumer
) )
observer.next(OrderProgressStep.LockedPayment) observer.next(OrderProgressStep.LockedPayment)
@ -352,7 +345,7 @@ export class OceanAssets extends Instantiable {
await oceanAgreements.create( await oceanAgreements.create(
did, did,
agreementId, agreementId,
serviceDefinitionId, index,
undefined, undefined,
consumer, consumer,
consumer consumer
@ -397,19 +390,35 @@ export class OceanAssets extends Instantiable {
* @param {string} owner Owner address. * @param {string} owner Owner address.
* @return {Promise<string[]>} List of DIDs. * @return {Promise<string[]>} List of DIDs.
*/ */
public async ownerAssets(owner: string) { public async ownerAssets(owner: string): Promise<string[]> {
return this.ocean.keeper.didRegistry.getAttributesByOwner(owner) return this.ocean.keeper.didRegistry.getAttributesByOwner(owner)
} }
/**
* Transfer ownership of an asset.
* @param {string} did Asset DID.
* @param {string} newOwner Ethereum address of the new owner of the DID.
* @return {Promise<TransactionReceipt>} Returns Web3 transaction receipt.
*/
public async transferOwnership(
did: string,
newOwner: string
): Promise<TransactionReceipt> {
const owner = await this.ocean.assets.owner(did)
return this.ocean.keeper.didRegistry.transferDIDOwnership(did, newOwner, owner)
}
/** /**
* Returns the assets of a consumer. * Returns the assets of a consumer.
* @param {string} consumer Consumer address. * @param {string} consumer Consumer address.
* @return {Promise<string[]>} List of DIDs. * @return {Promise<string[]>} List of DIDs.
*/ */
public async consumerAssets(consumer: string) { public async consumerAssets(consumer: string): Promise<string[]> {
return (await this.ocean.keeper.conditions.accessSecretStoreCondition.getGrantedDidByConsumer( return (
consumer await this.ocean.keeper.conditions.accessSecretStoreCondition.getGrantedDidByConsumer(
)).map(({ did }) => did) consumer
)
).map(({ did }) => did)
} }
/** /**

View File

@ -13,9 +13,7 @@ export class OceanAuth extends Instantiable {
* Returns the instance of OceanAuth. * Returns the instance of OceanAuth.
* @return {Promise<OceanAuth>} * @return {Promise<OceanAuth>}
*/ */
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<OceanAuth> {
config: InstantiableConfig
): Promise<OceanAuth> {
const instance = new OceanAuth() const instance = new OceanAuth()
instance.setInstanceConfig(config) instance.setInstanceConfig(config)

View File

@ -65,10 +65,10 @@ export class OceanSecretStore extends Instantiable {
consumer?: Account, consumer?: Account,
secretStoreUrl?: string secretStoreUrl?: string
): Promise<any> { ): Promise<any> {
return this.getSecretStoreByAccount( return this.getSecretStoreByAccount(consumer, secretStoreUrl).decryptDocument(
consumer, noDidPrefixed(did),
secretStoreUrl content
).decryptDocument(noDidPrefixed(did), content) )
} }
private getSecretStoreByAccount(account: Account, secretStoreUrl?: string) { private getSecretStoreByAccount(account: Account, secretStoreUrl?: string) {
@ -86,13 +86,7 @@ export class OceanSecretStore extends Instantiable {
} }
private getSecretStore(config: SecretStoreConfig): SecretStore { private getSecretStore(config: SecretStoreConfig): SecretStore {
const { const { secretStoreUri, parityUri, password, address, threshold } = config
secretStoreUri,
parityUri,
password,
address,
threshold
} = config
config = { secretStoreUri, parityUri, password, address, threshold } config = { secretStoreUri, parityUri, password, address, threshold }
return new SecretStore(config) return new SecretStore(config)

View File

@ -9,9 +9,7 @@ export class OceanTokens extends Instantiable {
* Returns the instance of OceanTokens. * Returns the instance of OceanTokens.
* @return {Promise<OceanTokens>} * @return {Promise<OceanTokens>}
*/ */
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<OceanTokens> {
config: InstantiableConfig
): Promise<OceanTokens> {
const instance = new OceanTokens() const instance = new OceanTokens()
instance.setInstanceConfig(config) instance.setInstanceConfig(config)
@ -20,22 +18,18 @@ export class OceanTokens extends Instantiable {
/** /**
* Transfer a number of tokens to the mentioned account. * Transfer a number of tokens to the mentioned account.
* @param {string} to Address that receives the account. * @param {string} to Address that receives the tokens.
* @param {number} amount Tokens to transfer. * @param {number} amount Tokens to transfer.
* @param {Account} from Sender account address. * @param {Account} from Sender account address.
* @return {Promise<boolean>} Success, * @return {Promise<boolean>} Success,
*/ */
public async transfer( public async transfer(to: string, amount: number, from: Account): Promise<boolean> {
to: string,
amount: number,
from: Account
): Promise<boolean> {
this.ocean.keeper.token.transfer(to, amount, from.getId()) this.ocean.keeper.token.transfer(to, amount, from.getId())
return true return true
} }
/** /**
* Request tokens for a account. * Request tokens for an account.
* @param {Account} account Account instance. * @param {Account} account Account instance.
* @param {number} amount Token amount. * @param {number} amount Token amount.
* @return {Promise<boolean>} Success. * @return {Promise<boolean>} Success.

View File

@ -42,9 +42,7 @@ export class OceanVersions extends Instantiable {
* Returns the instance of OceanVersions. * Returns the instance of OceanVersions.
* @return {Promise<OceanVersions>} * @return {Promise<OceanVersions>}
*/ */
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<OceanVersions> {
config: InstantiableConfig
): Promise<OceanVersions> {
const instance = new OceanVersions() const instance = new OceanVersions()
instance.setInstanceConfig(config) instance.setInstanceConfig(config)
@ -99,10 +97,7 @@ export class OceanVersions extends Instantiable {
// Aquarius // Aquarius
try { try {
const { const { software: name, version } = await this.ocean.aquarius.getVersionInfo()
software: name,
version
} = await this.ocean.aquarius.getVersionInfo()
versions.aquarius = { versions.aquarius = {
name, name,
status: OceanPlatformTechStatus.Working, status: OceanPlatformTechStatus.Working,
@ -124,9 +119,7 @@ export class OceanVersions extends Instantiable {
.reduce((acc, network) => ({ ...acc, [network]: true }), {}) .reduce((acc, network) => ({ ...acc, [network]: true }), {})
let contractStatus = true let contractStatus = true
const contractList = techs const contractList = techs.map(({ contracts }) => contracts).filter(_ => !!_)
.map(({ contracts }) => contracts)
.filter(_ => !!_)
Array.from(contractList.map(Object.keys)) Array.from(contractList.map(Object.keys))
.reduce((acc, _) => [...acc, ..._], []) .reduce((acc, _) => [...acc, ..._], [])
.filter((_, i, list) => list.indexOf(_) === i) .filter((_, i, list) => list.indexOf(_) === i)
@ -140,15 +133,14 @@ export class OceanVersions extends Instantiable {
return return
} }
if (address !== _) { if (address !== _) {
this.logger.warn(`Error on contract ${name}`)
contractStatus = false contractStatus = false
} }
}) })
}) })
versions.status = { versions.status = {
ok: !techs.find( ok: !techs.find(({ status }) => status !== OceanPlatformTechStatus.Working),
({ status }) => status !== OceanPlatformTechStatus.Working
),
network: Object.keys(networks).length === 1, network: Object.keys(networks).length === 1,
contracts: contractStatus contracts: contractStatus
} }

View File

@ -12,9 +12,7 @@ export class OceanUtils extends Instantiable {
* Returns the instance of OceanUtils. * Returns the instance of OceanUtils.
* @return {Promise<OceanUtils>} * @return {Promise<OceanUtils>}
*/ */
public static async getInstance( public static async getInstance(config: InstantiableConfig): Promise<OceanUtils> {
config: InstantiableConfig
): Promise<OceanUtils> {
const instance = new OceanUtils() const instance = new OceanUtils()
instance.setInstanceConfig(config) instance.setInstanceConfig(config)

View File

@ -13,20 +13,17 @@ export class ServiceAgreement extends Instantiable {
public async signServiceAgreement( public async signServiceAgreement(
ddo: DDO, ddo: DDO,
serviceDefinitionId: string, index: number,
serviceAgreementId: string, serviceAgreementId: string,
agreementConditionsIds: string[], agreementConditionsIds: string[],
consumer: Account consumer: Account
): Promise<string> { ): Promise<string> {
const service = ddo.findServiceById<'Access'>(serviceDefinitionId) const service = ddo.findServiceById<'access'>(index)
const timelockValues: number[] = this.getTimeValuesFromService( const timelockValues: number[] = this.getTimeValuesFromService(
service, service,
'timelock' 'timelock'
) )
const timeoutValues: number[] = this.getTimeValuesFromService( const timeoutValues: number[] = this.getTimeValuesFromService(service, 'timeout')
service,
'timeout'
)
if (!service.templateId) { if (!service.templateId) {
throw new Error('TemplateId not found in DDO.') throw new Error('TemplateId not found in DDO.')
@ -78,7 +75,7 @@ export class ServiceAgreement extends Instantiable {
timelocks: number[], timelocks: number[],
timeouts: number[] timeouts: number[]
): string { ): string {
const args = [ const args: any = [
{ type: 'address', value: zeroX(serviceAgreementTemplateId) }, { type: 'address', value: zeroX(serviceAgreementTemplateId) },
{ type: 'bytes32[]', value: valueHashes.map(zeroX) }, { type: 'bytes32[]', value: valueHashes.map(zeroX) },
{ type: 'uint256[]', value: timelocks }, { type: 'uint256[]', value: timelocks },
@ -86,14 +83,14 @@ export class ServiceAgreement extends Instantiable {
{ type: 'bytes32', value: zeroX(serviceAgreementId) } { type: 'bytes32', value: zeroX(serviceAgreementId) }
] ]
return this.web3.utils.soliditySha3(...args).toString('hex') return this.web3.utils.soliditySha3(...args)
} }
private getTimeValuesFromService( private getTimeValuesFromService(
service: ServiceAccess, service: ServiceAccess,
type: 'timeout' | 'timelock' type: 'timeout' | 'timelock'
): number[] { ): number[] {
const timeoutValues: number[] = service.serviceAgreementTemplate.conditions.map( const timeoutValues: number[] = service.attributes.serviceAgreementTemplate.conditions.map(
(condition: ServiceAgreementTemplateCondition) => condition[type] (condition: ServiceAgreementTemplateCondition) => condition[type]
) )

View File

@ -14,7 +14,7 @@ export class SignatureUtils extends Instantiable {
const isMetaMask = const isMetaMask =
this.web3 && this.web3 &&
this.web3.currentProvider && this.web3.currentProvider &&
this.web3.currentProvider.isMetaMask (this.web3.currentProvider as any).isMetaMask
try { try {
return await this.web3.eth.personal.sign(text, publicKey, password) return await this.web3.eth.personal.sign(text, publicKey, password)
} catch (e) { } catch (e) {

View File

@ -1,8 +1,8 @@
import fetch, { BodyInit, RequestInit, Response } from 'node-fetch' import { BodyInit, RequestInit, Response } from 'node-fetch'
import * as fs from 'fs' import fs from 'fs'
import { Instantiable, InstantiableConfig } from '../../Instantiable.abstract' import { Instantiable, InstantiableConfig } from '../../Instantiable.abstract'
const fetch = require('node-fetch')
import save = require('save-file') import save = require('save-file')
/** /**
@ -52,7 +52,7 @@ export class WebServiceConnector extends Instantiable {
if (!response.ok) { if (!response.ok) {
throw new Error('Response error.') throw new Error('Response error.')
} }
let filename let filename: string
try { try {
filename = response.headers filename = response.headers
.get('content-disposition') .get('content-disposition')
@ -69,9 +69,7 @@ export class WebServiceConnector extends Instantiable {
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
await new Promise(async (resolve, reject) => { await new Promise(async (resolve, reject) => {
fs.mkdirSync(destination, { recursive: true }) fs.mkdirSync(destination, { recursive: true })
const fileStream = fs.createWriteStream( const fileStream = fs.createWriteStream(`${destination}${filename}`)
`${destination}${filename}`
)
response.body.pipe(fileStream) response.body.pipe(fileStream)
response.body.on('error', reject) response.body.on('error', reject)
fileStream.on('finish', resolve) fileStream.on('finish', resolve)

View File

@ -24,14 +24,4 @@ export {
export { AgreementTemplate } from './keeper/contracts/templates' export { AgreementTemplate } from './keeper/contracts/templates'
export { Condition, ConditionState } from './keeper/contracts/conditions' export { Condition, ConditionState } from './keeper/contracts/conditions'
export { export { Ocean, Account, Config, DID, Logger, Keeper, conditions, templates, utils }
Ocean,
Account,
Config,
DID,
Logger,
Keeper,
conditions,
templates,
utils
}

View File

@ -36,9 +36,7 @@ function inputMatch(
if (typeof input !== 'string') { if (typeof input !== 'string') {
LoggerInstance.debug('Not input string:') LoggerInstance.debug('Not input string:')
LoggerInstance.debug(input) LoggerInstance.debug(input)
throw new Error( throw new Error(`[${conversorName}] Expected string, input type: ${typeof input}`)
`[${conversorName}] Expected string, input type: ${typeof input}`
)
} }
const match = input.match(regexp) const match = input.match(regexp)
if (!match) { if (!match) {

View File

@ -12,9 +12,7 @@ function fillParameterWithDDO(
switch (name) { switch (name) {
case 'amount': case 'amount':
case 'price': case 'price':
return String( return String(ddo.findServiceByType('metadata').attributes.main.price)
ddo.findServiceByType('Metadata').metadata.base.price
)
case 'assetId': case 'assetId':
case 'documentId': case 'documentId':
case 'documentKeyId': case 'documentKeyId':

View File

@ -8,9 +8,7 @@ const zipObject = (keys = [], values = []) => {
) )
} }
export const objectPromiseAll = async (obj: { export const objectPromiseAll = async (obj: { [key: string]: Promise<any> }) => {
[key: string]: Promise<any>
}) => {
const keys = Object.keys(obj) const keys = Object.keys(obj)
const result = await Promise.all(Object.values(obj)) const result = await Promise.all(Object.values(obj))
return zipObject(keys, result) return zipObject(keys, result)

View File

@ -40,9 +40,7 @@ export class SubscribableObserver<T, P> {
private emit(type: 'onNext' | 'onComplete' | 'onError', value: any) { private emit(type: 'onNext' | 'onComplete' | 'onError', value: any) {
Array.from(this.subscriptions) Array.from(this.subscriptions)
.map(subscription => subscription[type]) .map(subscription => subscription[type])
.filter( .filter((callback: any) => callback && typeof callback === 'function')
(callback: any) => callback && typeof callback === 'function'
)
.forEach((callback: any) => callback(value)) .forEach((callback: any) => callback(value))
} }

View File

@ -12,9 +12,7 @@ export class SubscribablePromise<T extends any, P extends any> {
this this
) )
constructor( constructor(executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>) {
executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>
) {
// Defear // Defear
setTimeout(() => this.init(executor), 1) setTimeout(() => this.init(executor), 1)
} }
@ -28,10 +26,7 @@ export class SubscribablePromise<T extends any, P extends any> {
return this return this
} }
public then( public then(onfulfilled?: (value: P) => any, onrejected?: (error: any) => any) {
onfulfilled?: (value: P) => any,
onrejected?: (error: any) => any
) {
return Object.assign(this.promise.then(onfulfilled, onrejected), this) return Object.assign(this.promise.then(onfulfilled, onrejected), this)
} }
@ -43,9 +38,7 @@ export class SubscribablePromise<T extends any, P extends any> {
return Object.assign(this.promise.finally(onfinally), this) return Object.assign(this.promise.finally(onfinally), this)
} }
private init( private init(executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>) {
executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>
) {
const execution = executor(this.observer) const execution = executor(this.observer)
Promise.resolve(execution as any) Promise.resolve(execution as any)

View File

@ -1,4 +1,4 @@
import * as assert from 'assert' import assert from 'assert'
import * as squid from '../src/squid' import * as squid from '../src/squid'
describe('Squid', () => { describe('Squid', () => {

View File

@ -1,5 +1,5 @@
import { assert, spy, use } from 'chai' import { assert, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import { Ocean } from '../../src/ocean/Ocean' import { Ocean } from '../../src/ocean/Ocean'
import { Aquarius, SearchQuery } from '../../src/aquarius/Aquarius' import { Aquarius, SearchQuery } from '../../src/aquarius/Aquarius'
import { DDO } from '../../src/ddo/DDO' import { DDO } from '../../src/ddo/DDO'
@ -22,7 +22,12 @@ describe('Aquarius', () => {
page = 0, page = 0,
total_pages = 1, total_pages = 1,
total_results = 1 total_results = 1
) => ({ results, page, total_pages, total_results }) ) => ({
results,
page,
total_pages,
total_results
})
/* eslint-enable @typescript-eslint/camelcase */ /* eslint-enable @typescript-eslint/camelcase */
beforeEach(async () => { beforeEach(async () => {
@ -48,9 +53,7 @@ describe('Aquarius', () => {
} as SearchQuery } as SearchQuery
it('should query metadata', async () => { it('should query metadata', async () => {
spy.on(ocean.utils.fetch, 'post', () => spy.on(ocean.utils.fetch, 'post', () => reponsify(getResults([new DDO()])))
reponsify(getResults([new DDO()]))
)
const result = await aquarius.queryMetadata(query) const result = await aquarius.queryMetadata(query)
assert.typeOf(result.results, 'array') assert.typeOf(result.results, 'array')
@ -61,9 +64,7 @@ describe('Aquarius', () => {
}) })
it('should query metadata and return real ddo', async () => { it('should query metadata and return real ddo', async () => {
spy.on(ocean.utils.fetch, 'post', () => spy.on(ocean.utils.fetch, 'post', () => reponsify(getResults([new DDO()])))
reponsify(getResults([new DDO()]))
)
const result = await aquarius.queryMetadata(query) const result = await aquarius.queryMetadata(query)
assert.typeOf(result.results, 'array') assert.typeOf(result.results, 'array')
@ -86,9 +87,7 @@ describe('Aquarius', () => {
} as SearchQuery } as SearchQuery
it('should query metadata by text', async () => { it('should query metadata by text', async () => {
spy.on(ocean.utils.fetch, 'get', () => spy.on(ocean.utils.fetch, 'get', () => reponsify(getResults([new DDO()])))
reponsify(getResults([new DDO()]))
)
const result = await aquarius.queryMetadataByText(query) const result = await aquarius.queryMetadataByText(query)
assert.typeOf(result.results, 'array') assert.typeOf(result.results, 'array')
@ -99,9 +98,7 @@ describe('Aquarius', () => {
}) })
it('should query metadata and return real ddo', async () => { it('should query metadata and return real ddo', async () => {
spy.on(ocean.utils.fetch, 'get', () => spy.on(ocean.utils.fetch, 'get', () => reponsify(getResults([new DDO()])))
reponsify(getResults([new DDO()]))
)
const result = await aquarius.queryMetadataByText(query) const result = await aquarius.queryMetadataByText(query)
assert.typeOf(result.results, 'array') assert.typeOf(result.results, 'array')

View File

@ -1,6 +1,6 @@
import { assert, expect, spy, use } from 'chai' import { assert, expect, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import * as Web3 from 'web3' import Web3 from 'web3'
import { DDO } from '../../src/ddo/DDO' import { DDO } from '../../src/ddo/DDO'
import { Service } from '../../src/ddo/Service' import { Service } from '../../src/ddo/Service'
@ -46,8 +46,7 @@ describe('DDO', () => {
}, },
{ {
type: 'CredentialRepositoryService', type: 'CredentialRepositoryService',
serviceEndpoint: serviceEndpoint: 'https://repository.example.com/service/8377464'
'https://repository.example.com/service/8377464'
}, },
{ {
type: 'XdiService', type: 'XdiService',
@ -77,29 +76,58 @@ describe('DDO', () => {
serviceEndpoint: 'https://bops.example.com/enterprise/' serviceEndpoint: 'https://bops.example.com/enterprise/'
}, },
{ {
type: 'Consume', type: 'consume',
serviceEndpoint: serviceEndpoint:
'http://mybrizo.org/api/v1/brizo/services/consume?pubKey={pubKey}&serviceId={serviceId}&url={url}' 'http://mybrizo.org/api/v1/brizo/services/consume?pubKey={pubKey}&serviceId={serviceId}&url={url}'
}, },
{ {
type: 'Compute', type: 'compute',
serviceEndpoint: serviceEndpoint:
'http://mybrizo.org/api/v1/brizo/services/compute?pubKey={pubKey}&serviceId={serviceId}&algo={algo}&container={container}' 'http://mybrizo.org/api/v1/brizo/services/compute?pubKey={pubKey}&serviceId={serviceId}&algo={algo}&container={container}'
}, },
{ {
type: 'Metadata', type: 'metadata',
index: 0,
serviceEndpoint: serviceEndpoint:
'http://myaquarius.org/api/v1/provider/assets/metadata/{did}', 'http://myaquarius.org/api/v1/provider/assets/metadata/{did}',
metadata: { attributes: {
base: { main: {
name: 'UK Weather information 2011', name: 'UK Weather information 2011',
type: 'dataset', type: 'dataset',
description:
'Weather information of UK including temperature and humidity',
dateCreated: '2012-10-10T17:00:000Z', dateCreated: '2012-10-10T17:00:000Z',
datePublished: '2012-10-10T17:00:000Z', datePublished: '2012-10-10T17:00:000Z',
author: 'Met Office', author: 'Met Office',
license: 'CC-BY', license: 'CC-BY',
price: '10',
files: [
{
index: 0,
checksum: 'efb2c764274b745f5fc37f97c6b0e761',
contentLength: '4535431',
contentType: 'application/json',
resourceId:
'access-log2018-02-13-15-17-29-18386C502CAEA932'
},
{
index: 1,
checksum: '085340abffh21495345af97c6b0e761',
contentLength: '12324',
contentType: 'application/json'
},
{
index: 2,
contentType: ''
}
]
},
curation: {
rating: 0.93,
numVotes: 123,
schema: 'Binary Voting'
},
additionalInformation: {
description:
'Weather information of UK including temperature and humidity',
copyrightHolder: 'Met Office', copyrightHolder: 'Met Office',
workExample: workExample:
'423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68', '423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68',
@ -113,55 +141,20 @@ describe('DDO', () => {
'http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/' 'http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/'
}, },
{ {
fieldsDescription: fieldsDescription: 'http://data.ceda.ac.uk/badc/ukcp09/'
'http://data.ceda.ac.uk/badc/ukcp09/'
} }
], ],
inLanguage: 'en', inLanguage: 'en',
categories: ['Economy', 'Data Science'], categories: ['Economy', 'Data Science'],
tags: [ tags: ['weather', 'uk', '2011', 'temperature', 'humidity'],
'weather', updateFrequency: 'yearly',
'uk',
'2011',
'temperature',
'humidity'
],
price: 10,
files: [
{
index: 0,
checksum: 'efb2c764274b745f5fc37f97c6b0e761',
contentLength: 4535431,
resourceId:
'access-log2018-02-13-15-17-29-18386C502CAEA932'
},
{
index: 1,
checksum: '085340abffh21495345af97c6b0e761',
contentLength: 12324
},
{
index: 2
}
],
checksum: ''
},
curation: {
rating: 0.93,
numVotes: 123,
schema: 'Binary Voting'
},
additionalInformation: {
updateFrecuency: 'yearly',
structuredMarkup: [ structuredMarkup: [
{ {
uri: uri: 'http://skos.um.es/unescothes/C01194/jsonld',
'http://skos.um.es/unescothes/C01194/jsonld',
mediaType: 'application/ld+json' mediaType: 'application/ld+json'
}, },
{ {
uri: uri: 'http://skos.um.es/unescothes/C01194/turtle',
'http://skos.um.es/unescothes/C01194/turtle',
mediaType: 'text/turtle' mediaType: 'text/turtle'
} }
] ]
@ -230,10 +223,7 @@ describe('DDO', () => {
assert.instanceOf(ddo, DDO) assert.instanceOf(ddo, DDO)
assert.equal(ddo.id, testDDO.id) assert.equal(ddo.id, testDDO.id)
assert.equal( assert.equal(ddo.publicKey[0].publicKeyPem, testDDO.publicKey[0].publicKeyPem)
ddo.publicKey[0].publicKeyPem,
testDDO.publicKey[0].publicKeyPem
)
}) })
it('should properly deserialize from json file', async () => { it('should properly deserialize from json file', async () => {
@ -241,10 +231,7 @@ describe('DDO', () => {
assert(ddo) assert(ddo)
assert.equal(ddo.id, jsonDDO.id) assert.equal(ddo.id, jsonDDO.id)
assert.equal( assert.equal(ddo.publicKey[0].publicKeyPem, jsonDDO.publicKey[0].publicKeyPem)
ddo.publicKey[0].publicKeyPem,
jsonDDO.publicKey[0].publicKeyPem
)
}) })
}) })
@ -265,11 +252,7 @@ describe('DDO', () => {
const signature = `0x${'a'.repeat(130)}` const signature = `0x${'a'.repeat(130)}`
it('should properly generate the proof', async () => { it('should properly generate the proof', async () => {
const signTextSpy = spy.on( const signTextSpy = spy.on(ocean.utils.signature, 'signText', () => signature)
ocean.utils.signature,
'signText',
() => signature
)
const ddo = new DDO(testDDO) const ddo = new DDO(testDDO)
const checksum = ddo.getChecksum() const checksum = ddo.getChecksum()
const proof = await ddo.generateProof(ocean, publicKey) const proof = await ddo.generateProof(ocean, publicKey)
@ -294,12 +277,8 @@ describe('DDO', () => {
signaturValue: 'test' signaturValue: 'test'
} as any } as any
const ddo = new DDO(testDDO) const ddo = new DDO(testDDO)
const generateProofSpy = spy.on( const generateProofSpy = spy.on(ddo, 'generateProof', () => fakeProof)
ddo, await ddo.addProof(ocean, publicKey)
'generateProof',
() => fakeProof
)
await ddo.addProof(web3, publicKey)
assert.equal(ddo.proof, fakeProof) assert.equal(ddo.proof, fakeProof)
expect(generateProofSpy).to.have.been.called.with(publicKey) expect(generateProofSpy).to.have.been.called.with(publicKey)

View File

@ -30,11 +30,9 @@ describe('ContractWrapperBase', () => {
}) })
it('should fail to call on an unknown contract function', done => { it('should fail to call on an unknown contract function', done => {
wrappedContract wrappedContract.sendMock('balanceOfxxx', '0x00', ['0x00']).catch(() => {
.sendMock('balanceOfxxx', '0x00', ['0x00']) done()
.catch(() => { })
done()
})
}) })
it('should fail to call on an contract function with wrong set of parameters', done => { it('should fail to call on an contract function with wrong set of parameters', done => {
@ -46,11 +44,9 @@ describe('ContractWrapperBase', () => {
describe('#send()', () => { describe('#send()', () => {
it('should fail to call on an unknown contract function', done => { it('should fail to call on an unknown contract function', done => {
wrappedContract wrappedContract.sendMock('transferxxx', accounts[0].getId(), []).catch(() => {
.sendMock('transferxxx', accounts[0].getId(), []) done()
.catch(() => { })
done()
})
}) })
}) })

View File

@ -17,17 +17,14 @@ describe('ContractEvent', () => {
eventHandler = new EventHandler((ocean as any).instanceConfig) eventHandler = new EventHandler((ocean as any).instanceConfig)
account = (await ocean.accounts.list())[0].getId() account = (await ocean.accounts.list())[0].getId()
executeTransaction = () => executeTransaction = () => ocean.keeper.dispenser.requestTokens(10, account)
ocean.keeper.dispenser.requestTokens(10, account)
}) })
describe('#subscribe()', () => { describe('#subscribe()', () => {
it('should be able to listen to events', async () => { it('should be able to listen to events', async () => {
const event = eventHandler.getEvent( const event = eventHandler.getEvent(ocean.keeper.token, 'Transfer', {
ocean.keeper.token, to: account
'Transfer', })
{ to: account }
)
let validResolve = false let validResolve = false
let subscription: ContractEventSubscription let subscription: ContractEventSubscription
@ -57,11 +54,7 @@ describe('ContractEvent', () => {
describe('#once()', () => { describe('#once()', () => {
it('should listen to event only once', async () => { it('should listen to event only once', async () => {
const to = account const to = account
const event = eventHandler.getEvent( const event = eventHandler.getEvent(ocean.keeper.token, 'Transfer', { to })
ocean.keeper.token,
'Transfer',
{ to }
)
let canBeRejected = false let canBeRejected = false
const waitUntilEvent = new Promise((resolve, reject) => { const waitUntilEvent = new Promise((resolve, reject) => {
@ -85,11 +78,7 @@ describe('ContractEvent', () => {
it('should get the event like a promise', async () => { it('should get the event like a promise', async () => {
const to = account const to = account
const event = eventHandler.getEvent( const event = eventHandler.getEvent(ocean.keeper.token, 'Transfer', { to })
ocean.keeper.token,
'Transfer',
{ to }
)
const waitUntilEvent = event.once() const waitUntilEvent = event.once()

View File

@ -13,7 +13,7 @@ describe('DIDRegistry', () => {
before(async () => { before(async () => {
await TestContractHandler.prepareContracts() await TestContractHandler.prepareContracts()
ocean = await Ocean.getInstance(config) ocean = await Ocean.getInstance(config)
didRegistry = ocean.keeper.didRegistry ;({ didRegistry } = ocean.keeper)
}) })
describe('#registerAttribute()', () => { describe('#registerAttribute()', () => {
@ -89,4 +89,36 @@ describe('DIDRegistry', () => {
assert.equal(owner, `0x${'0'.repeat(40)}`) assert.equal(owner, `0x${'0'.repeat(40)}`)
}) })
}) })
describe('#transferDIDOwnership()', () => {
it('should be able to transfer ownership', async () => {
// create and register DID
const ownerAccount: Account = (await ocean.accounts.list())[0]
const did = generateId()
const data = 'my nice provider, is nice'
await didRegistry.registerAttribute(
did,
'0123456789abcdef',
[],
data,
ownerAccount.getId()
)
// transfer
const newOwnerAccount: Account = (await ocean.accounts.list())[1]
await didRegistry.transferDIDOwnership(
did,
newOwnerAccount.getId(),
ownerAccount.getId()
)
// check
const newOwner = await didRegistry.getDIDOwner(did)
assert.equal(
newOwner,
newOwnerAccount.getId(),
`Got ${newOwner} but expected ${newOwnerAccount.getId()}`
)
})
})
}) })

View File

@ -1,5 +1,5 @@
import { assert, expect, spy, use } from 'chai' import { assert, expect, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import { EventHandler } from '../../src/keeper/EventHandler' import { EventHandler } from '../../src/keeper/EventHandler'
import { Ocean } from '../../src/ocean/Ocean' import { Ocean } from '../../src/ocean/Ocean'
import config from '../config' import config from '../config'
@ -27,11 +27,7 @@ describe('EventHandler', () => {
assert.isDefined(subscription) assert.isDefined(subscription)
const countAfter = eventHandler.count const countAfter = eventHandler.count
assert.equal( assert.equal(countBefore + 1, countAfter, 'The event seems not added.')
countBefore + 1,
countAfter,
'The event seems not added.'
)
subscription.unsubscribe() subscription.unsubscribe()
}) })
@ -58,11 +54,7 @@ describe('EventHandler', () => {
eventHandler.unsubscribe(callback) eventHandler.unsubscribe(callback)
const countAfter = eventHandler.count const countAfter = eventHandler.count
assert.equal( assert.equal(countBefore, countAfter, 'The event seems not removed.')
countBefore,
countAfter,
'The event seems not removed.'
)
}) })
}) })

View File

@ -1,9 +1,14 @@
import Contract from 'web3-eth-contract' import { Contract } from 'web3-eth-contract'
import ContractHandler from '../../src/keeper/ContractHandler' import ContractHandler from '../../src/keeper/ContractHandler'
import Web3Provider from '../../src/keeper/Web3Provider' import Web3Provider from '../../src/keeper/Web3Provider'
import Logger from '../../src/utils/Logger' import Logger from '../../src/utils/Logger'
import config from '../config' import config from '../config'
interface ContractTest extends Contract {
testContract?: boolean
$initialized?: boolean
}
export default class TestContractHandler extends ContractHandler { export default class TestContractHandler extends ContractHandler {
public static async prepareContracts() { public static async prepareContracts() {
const web3 = Web3Provider.getWeb3(config) const web3 = Web3Provider.getWeb3(config)
@ -134,32 +139,30 @@ export default class TestContractHandler extends ContractHandler {
from: string, from: string,
args: any[] = [], args: any[] = [],
tokens: { [name: string]: string } = {} tokens: { [name: string]: string } = {}
): Promise<Contract & { $initialized: boolean }> { ): Promise<ContractTest> {
const where = this.networkId const where = this.networkId
// dont redeploy if there is already something loaded // dont redeploy if there is already something loaded
if (TestContractHandler.hasContract(name, where)) { if (TestContractHandler.hasContract(name, where)) {
const contract = await ContractHandler.getContract(name, where) const contract: ContractTest = await ContractHandler.getContract(name, where)
if (contract.testContract) { if (contract.testContract) {
return { ...contract, $initialized: true } return { ...contract, $initialized: true } as any
} }
} }
const web3 = Web3Provider.getWeb3(config) const web3 = Web3Provider.getWeb3(config)
let contractInstance: Contract let contractInstance: ContractTest
try { try {
Logger.log('Deploying', name) Logger.log('Deploying', name)
const sendConfig = { const sendConfig = {
from, from,
gas: 3000000, gas: 3000000,
gasPrice: 10000000000 gasPrice: '10000000000'
} }
// eslint-disable-next-line security/detect-non-literal-require
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${name}.development.json`) const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${name}.development.json`)
const tempContract = new web3.eth.Contract( const tempContract = new web3.eth.Contract(artifact.abi, artifact.address)
artifact.abi,
artifact.address
)
const isZos = !!tempContract.methods.initialize const isZos = !!tempContract.methods.initialize
Logger.debug({ Logger.debug({
@ -184,13 +187,11 @@ export default class TestContractHandler extends ContractHandler {
}) })
.send(sendConfig) .send(sendConfig)
if (isZos) { if (isZos) {
await contractInstance.methods await contractInstance.methods.initialize(...args).send(sendConfig)
.initialize(...args)
.send(sendConfig)
} }
contractInstance.testContract = true contractInstance.testContract = true
ContractHandler.setContract(name, where, contractInstance) ContractHandler.setContract(name, where, contractInstance)
// Logger.log("Deployed", name, "at", contractInstance.options.address); // Logger.log('Deployed', name, 'at', contractInstance.options.address)
} catch (err) { } catch (err) {
Logger.error( Logger.error(
'Deployment failed for', 'Deployment failed for',

View File

@ -4,7 +4,7 @@ export default class BrizoMock extends Brizo {
public async initializeServiceAgreement( public async initializeServiceAgreement(
did: string, did: string,
serviceAgreementId: string, serviceAgreementId: string,
serviceDefinitionId: string, index: number,
signature: string, signature: string,
consumerPublicKey: string consumerPublicKey: string
): Promise<any> { ): Promise<any> {

View File

@ -1,4 +1,4 @@
import * as assert from 'assert' import assert from 'assert'
import DID from '../../src/ocean/DID' import DID from '../../src/ocean/DID'
describe('DID', () => { describe('DID', () => {

View File

@ -1,7 +1,6 @@
import { assert, spy, use } from 'chai' import { assert, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import { SearchQuery } from '../../src/aquarius/Aquarius'
import Account from '../../src/ocean/Account' import Account from '../../src/ocean/Account'
import { Ocean } from '../../src/ocean/Ocean' import { Ocean } from '../../src/ocean/Ocean'
import config from '../config' import config from '../config'
@ -41,33 +40,4 @@ describe('Ocean', () => {
assert(typeof accs[0].getId() === 'string') assert(typeof accs[0].getId() === 'string')
}) })
}) })
describe('#searchAssets()', () => {
it('should search for assets', async () => {
const query = {
offset: 100,
page: 1,
query: {
value: 1
},
sort: {
value: 1
},
text: 'Office'
} as SearchQuery
const assets = await ocean.assets.query(query)
assert(assets)
})
})
describe('#searchAssetsByText()', () => {
it('should search for assets', async () => {
const text = 'office'
const assets = await ocean.assets.search(text)
assert(assets)
})
})
}) })

View File

@ -1,5 +1,5 @@
import { assert, spy, use } from 'chai' import { assert, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import config from '../config' import config from '../config'
import Account from '../../src/ocean/Account' import Account from '../../src/ocean/Account'

View File

@ -0,0 +1,48 @@
import { assert, spy, use } from 'chai'
import spies from 'chai-spies'
import { SearchQuery } from '../../src/aquarius/Aquarius'
import { Ocean } from '../../src/ocean/Ocean'
import config from '../config'
use(spies)
let ocean: Ocean
describe('OceanAssets', () => {
before(async () => {
ocean = await Ocean.getInstance(config)
})
afterEach(() => {
spy.restore()
})
describe('#query()', () => {
it('should search for assets', async () => {
const query: SearchQuery = {
offset: 100,
page: 1,
query: {
text: 'Office'
},
sort: {
created: -1
}
} as SearchQuery
const assets = await ocean.assets.query(query)
assert(assets)
})
})
describe('#search()', () => {
it('should search for assets', async () => {
const text = 'office'
const assets = await ocean.assets.search(text)
assert(assets)
})
})
})

View File

@ -1,5 +1,5 @@
import { assert, expect, spy, use } from 'chai' import { assert, expect, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import config from '../config' import config from '../config'
import Account from '../../src/ocean/Account' import Account from '../../src/ocean/Account'
@ -54,11 +54,7 @@ describe('OceanAuth', () => {
describe('#store()', () => { describe('#store()', () => {
it('should sign and store the token', async () => { it('should sign and store the token', async () => {
const writeTokenSpy = spy.on( const writeTokenSpy = spy.on(oceanAuth as any, 'writeToken', () => null)
oceanAuth as any,
'writeToken',
() => {}
)
await oceanAuth.store(account) await oceanAuth.store(account)

View File

@ -1,5 +1,5 @@
import { assert, expect, spy, use } from 'chai' import { assert, expect, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import Account from '../../src/ocean/Account' import Account from '../../src/ocean/Account'
import { Ocean } from '../../src/ocean/Ocean' import { Ocean } from '../../src/ocean/Ocean'
@ -33,11 +33,7 @@ describe('OceanSecretStore', () => {
() => 'encryptedResult' () => 'encryptedResult'
) )
const result = await oceanSecretStore.encrypt( const result = await oceanSecretStore.encrypt(did, 'test', accounts[0])
did,
'test',
accounts[0]
)
expect(secretStoreEncryptSpy).to.have.been.called.with(did, 'test') expect(secretStoreEncryptSpy).to.have.been.called.with(did, 'test')

View File

@ -1,6 +1,6 @@
import { assert, expect, spy, use } from 'chai' import { assert, expect, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import Web3 from 'web3'
import config from '../../config' import config from '../../config'
import { Ocean } from '../../../src/ocean/Ocean' import { Ocean } from '../../../src/ocean/Ocean'
@ -11,7 +11,7 @@ describe('SignatureUtils', () => {
const publicKey = `0x${'a'.repeat(40)}` const publicKey = `0x${'a'.repeat(40)}`
const text = '0123456789abcde' const text = '0123456789abcde'
const signature = `0x${'a'.repeat(130)}` const signature = `0x${'a'.repeat(130)}`
let web3 let web3: Web3
let ocean: Ocean let ocean: Ocean
before(async () => { before(async () => {
@ -38,18 +38,10 @@ describe('SignatureUtils', () => {
}) })
it('should sign a text as expected using password', async () => { it('should sign a text as expected using password', async () => {
const signed = await ocean.utils.signature.signText( const signed = await ocean.utils.signature.signText(text, publicKey, 'test')
text,
publicKey,
'test'
)
assert.equal(signed, signature) assert.equal(signed, signature)
expect(personalSignSpy).to.have.been.called.with( expect(personalSignSpy).to.have.been.called.with(text, publicKey, 'test')
text,
publicKey,
'test'
)
}) })
}) })

View File

@ -39,8 +39,8 @@
}, },
"service": [ "service": [
{ {
"type": "Access", "type": "access",
"serviceDefinitionId": "0", "index": 0,
"serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/consume?pubKey=${pubKey}&serviceId={serviceId}&url={url}", "serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/consume?pubKey=${pubKey}&serviceId={serviceId}&url={url}",
"purchaseEndpoint": "http://mybrizo.org/api/v1/brizo/services/access/purchase?", "purchaseEndpoint": "http://mybrizo.org/api/v1/brizo/services/access/purchase?",
"templateId": "044852b2a670ade5407e78fb2863c51000000000000000000000000000000000", "templateId": "044852b2a670ade5407e78fb2863c51000000000000000000000000000000000",
@ -58,9 +58,7 @@
}, },
"events": { "events": {
"PaymentLocked": { "PaymentLocked": {
"actorType": [ "actorType": ["publisher"],
"publisher"
],
"handlers": [ "handlers": [
{ {
"moduleName": "accessControl", "moduleName": "accessControl",
@ -84,9 +82,7 @@
}, },
"events": { "events": {
"PaymentReleased": { "PaymentReleased": {
"actorType": [ "actorType": ["publisher"],
"publisher"
],
"handlers": [ "handlers": [
{ {
"moduleName": "serviceAgreement", "moduleName": "serviceAgreement",
@ -110,9 +106,7 @@
}, },
"events": { "events": {
"AccessGranted": { "AccessGranted": {
"actorType": [ "actorType": ["consumer"],
"consumer"
],
"handlers": [ "handlers": [
{ {
"moduleName": "asset", "moduleName": "asset",
@ -136,9 +130,7 @@
}, },
"events": { "events": {
"PaymentRefund": { "PaymentRefund": {
"actorType": [ "actorType": ["consumer"],
"consumer"
],
"handlers": [ "handlers": [
{ {
"moduleName": "serviceAgreement", "moduleName": "serviceAgreement",
@ -152,39 +144,44 @@
] ]
}, },
{ {
"type": "CloudCompute", "type": "compute",
"serviceDefinitionId": "1", "index": 1,
"serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/compute?pubKey=${pubKey}&serviceId={serviceId}&algo={algo}&container={container}", "serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/compute?pubKey=${pubKey}&serviceId={serviceId}&algo={algo}&container={container}",
"templateId": "044852b2a670ade5407e78fb2863c51000000000000000000000000000000002" "templateId": "044852b2a670ade5407e78fb2863c51000000000000000000000000000000002"
}, },
{ {
"type": "Metadata", "type": "metadata",
"serviceDefinitionId": "2", "index": 2,
"serviceEndpoint": "http://myaquarius.org/api/v1/provider/assets/metadata/{did}", "serviceEndpoint": "http://myaquarius.org/api/v1/provider/assets/metadata/{did}",
"metadata": { "attributes": {
"base": { "main": {
"name": "UK Weather information 2011", "name": "UK Weather information 2011",
"type": "dataset", "type": "dataset",
"description": "Weather information of UK including temperature and humidity",
"dateCreated": "2012-10-10T17:00:000Z", "dateCreated": "2012-10-10T17:00:000Z",
"author": "Met Office", "author": "Met Office",
"license": "CC-BY", "license": "CC-BY",
"copyrightHolder": "Met Office", "price": 10,
"workExample": "423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
"contentUrls": [
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip"
],
"files": [ "files": [
{ {
"index": 0, "index": 0,
"url": "https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip", "url": "https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
"checksum": "085340abffh21495345af97c6b0e761", "checksum": "085340abffh21495345af97c6b0e761",
"contentLength": 12324 "contentLength": "12324"
}, },
{ {
"url": "https://testocnfiles.blob.core.windows.net/testfiles/testzkp2.zip" "url": "https://testocnfiles.blob.core.windows.net/testfiles/testzkp2.zip"
} }
], ]
},
"curation": {
"rating": 0.93,
"numVotes": 123,
"schema": "Binary Voting"
},
"additionalInformation": {
"description": "Weather information of UK including temperature and humidity",
"copyrightHolder": "Met Office",
"workExample": "423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
"links": [ "links": [
{ {
"name": "Sample of Asset Data", "name": "Sample of Asset Data",
@ -200,14 +197,6 @@
"inLanguage": "en", "inLanguage": "en",
"categories": ["Economy", "Data Science"], "categories": ["Economy", "Data Science"],
"tags": ["weather", "uk", "2011", "temperature", "humidity"], "tags": ["weather", "uk", "2011", "temperature", "humidity"],
"price": 10
},
"curation": {
"rating": 0.93,
"numVotes": 123,
"schema": "Binary Voting"
},
"additionalInformation": {
"updateFrequency": "yearly", "updateFrequency": "yearly",
"structuredMarkup": [ "structuredMarkup": [
{ {

View File

@ -2,6 +2,8 @@
"compilerOptions": { "compilerOptions": {
"resolveJsonModule": true, "resolveJsonModule": true,
"lib": ["es6", "es7"], "lib": ["es6", "es7"],
"noUnusedLocals": true "noUnusedLocals": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
} }
} }

View File

@ -1,5 +1,5 @@
import { assert, expect, spy, use } from 'chai' import { assert, expect, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import { SubscribableObserver } from '../../src/utils/SubscribableObserver' import { SubscribableObserver } from '../../src/utils/SubscribableObserver'

View File

@ -1,5 +1,5 @@
import { assert, expect, spy, use } from 'chai' import { assert, expect, spy, use } from 'chai'
import * as spies from 'chai-spies' import spies from 'chai-spies'
import { SubscribablePromise } from '../../src/utils/SubscribablePromise' import { SubscribablePromise } from '../../src/utils/SubscribablePromise'
@ -7,15 +7,15 @@ use(spies)
describe('SubscribablePromise', () => { describe('SubscribablePromise', () => {
it('should work', async () => { it('should work', async () => {
const subscribible = new SubscribablePromise(() => {}) const subscribable = new SubscribablePromise(() => null)
assert.isDefined(subscribible) assert.isDefined(subscribable)
}) })
describe('#subscribe()', () => { describe('#subscribe()', () => {
it('should return a subscription', async () => { it('should return a subscription', async () => {
const subscribible = new SubscribablePromise(() => {}) const subscribable = new SubscribablePromise(() => null)
const subscription = subscribible.subscribe(() => {}) const subscription = subscribable.subscribe(() => null)
assert.isDefined(subscription) assert.isDefined(subscription)
assert.isDefined(subscription.unsubscribe) assert.isDefined(subscription.unsubscribe)
@ -24,11 +24,11 @@ describe('SubscribablePromise', () => {
it('should listen the next values', done => { it('should listen the next values', done => {
const onNextSpy = spy() const onNextSpy = spy()
const subscribible = new SubscribablePromise(observer => { const subscribable = new SubscribablePromise(observer => {
setTimeout(() => observer.next('test'), 10) setTimeout(() => observer.next('test'), 10)
setTimeout(() => observer.next('test'), 20) setTimeout(() => observer.next('test'), 20)
}) })
subscribible.subscribe(onNextSpy) subscribable.subscribe(onNextSpy)
setTimeout(() => { setTimeout(() => {
expect(onNextSpy).to.has.been.called.with('test') expect(onNextSpy).to.has.been.called.with('test')
@ -42,12 +42,12 @@ describe('SubscribablePromise', () => {
it('should resolve', done => { it('should resolve', done => {
const onCompleteSpy = spy() const onCompleteSpy = spy()
const onFinallySpy = spy() const onFinallySpy = spy()
const subscribible = new SubscribablePromise(observer => { const subscribable = new SubscribablePromise(observer => {
setTimeout(() => observer.next('test'), 10) setTimeout(() => observer.next('test'), 10)
setTimeout(() => observer.complete('test'), 20) setTimeout(() => observer.complete('test'), 20)
}) })
subscribible.then(onCompleteSpy).finally(onFinallySpy) subscribable.then(onCompleteSpy).finally(onFinallySpy)
setTimeout(() => { setTimeout(() => {
expect(onCompleteSpy).to.has.been.called.with('test') expect(onCompleteSpy).to.has.been.called.with('test')
@ -62,12 +62,12 @@ describe('SubscribablePromise', () => {
it('should catch the error', done => { it('should catch the error', done => {
const onErrorSpy = spy() const onErrorSpy = spy()
const onFinallySpy = spy() const onFinallySpy = spy()
const subscribible = new SubscribablePromise(observer => { const subscribable = new SubscribablePromise(observer => {
setTimeout(() => observer.next('test'), 10) setTimeout(() => observer.next('test'), 10)
setTimeout(() => observer.error('test'), 20) setTimeout(() => observer.error('test'), 20)
}) })
subscribible.catch(onErrorSpy).finally(onFinallySpy) subscribable.catch(onErrorSpy).finally(onFinallySpy)
setTimeout(() => { setTimeout(() => {
expect(onErrorSpy).to.has.been.called.with('test') expect(onErrorSpy).to.has.been.called.with('test')
@ -80,13 +80,13 @@ describe('SubscribablePromise', () => {
it('should be able to subscribe and wait for a promise', async () => { it('should be able to subscribe and wait for a promise', async () => {
const onNextSpy = spy() const onNextSpy = spy()
const subscribible = new SubscribablePromise(observer => { const subscribable = new SubscribablePromise(observer => {
setTimeout(() => observer.next('test'), 10) setTimeout(() => observer.next('test'), 10)
setTimeout(() => observer.next('test'), 20) setTimeout(() => observer.next('test'), 20)
setTimeout(() => observer.complete('completed'), 30) setTimeout(() => observer.complete('completed'), 30)
}) })
const result = await subscribible.next(onNextSpy) const result = await subscribable.next(onNextSpy)
expect(onNextSpy).to.has.been.called.with('test') expect(onNextSpy).to.has.been.called.with('test')
expect(onNextSpy).to.has.been.called.exactly(2) expect(onNextSpy).to.has.been.called.exactly(2)
@ -96,7 +96,7 @@ describe('SubscribablePromise', () => {
it('should use the result of a the promise as executor to complete the observer', async () => { it('should use the result of a the promise as executor to complete the observer', async () => {
const onNextSpy = spy() const onNextSpy = spy()
const subscribible = new SubscribablePromise(async observer => { const subscribable = new SubscribablePromise(async observer => {
await new Promise(resolve => setTimeout(resolve, 10)) await new Promise(resolve => setTimeout(resolve, 10))
observer.next('test') observer.next('test')
await new Promise(resolve => setTimeout(resolve, 10)) await new Promise(resolve => setTimeout(resolve, 10))
@ -105,7 +105,7 @@ describe('SubscribablePromise', () => {
return 'completed' return 'completed'
}) })
const result = await subscribible.next(onNextSpy) const result = await subscribable.next(onNextSpy)
expect(onNextSpy).to.has.been.called.with('test') expect(onNextSpy).to.has.been.called.with('test')
expect(onNextSpy).to.has.been.called.exactly(2) expect(onNextSpy).to.has.been.called.exactly(2)

View File

@ -2,6 +2,8 @@
"compilerOptions": { "compilerOptions": {
"resolveJsonModule": true, "resolveJsonModule": true,
"moduleResolution": "node", "moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"lib": ["es2017", "es6", "es7", "dom"], "lib": ["es2017", "es6", "es7", "dom"],
"declaration": true, "declaration": true,
"module": "commonjs", "module": "commonjs",