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,
"singleQuote": true,
"printWidth": 90,
"trailingComma": "none"
}

View File

@ -2,7 +2,7 @@ dist: xenial
sudo: required
language: node_js
node_js:
- '11'
- '12'
services:
- docker
@ -21,10 +21,10 @@ before_script:
- ganache-cli --port 18545 > ganache-cli.log &
- git clone https://github.com/oceanprotocol/barge
- cd barge
- export AQUARIUS_VERSION=v0.3.8
- export BRIZO_VERSION=v0.4.5
- export AQUARIUS_VERSION=v1.0.5
- export BRIZO_VERSION=v0.7.2
- 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"
- rm -rf "${HOME}/.ocean/keeper-contracts/artifacts"
- bash -x start_ocean.sh --no-commons --no-dashboard 2>&1 > start_ocean.log &
@ -32,10 +32,10 @@ before_script:
script:
- npm run lint
- ./scripts/keeper.sh
- export ETH_PORT=18545; npm run test:cover
- npm run build
- npm run doc
- ./scripts/keeper.sh
- npm run integration:cover
- 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).
#### [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)
> 22 November 2019
- 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)

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)
- [Examples](#examples)
- [Documentation](#documentation)
- [Migration Guide](#migration-guide)
- [Development](#development)
- [Testing](#testing)
- [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:
- [Docs: React Tutorial](https://docs.oceanprotocol.com/tutorials/react-setup/)
- [Integration test](/src/integration/ocean/)
- [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
```
### Migration Guide
Instructions on how to migrate between breaking versions:
- [Migration Guide](MIGRATION.md)
## Development
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'
const HDWalletProvider = require('@truffle/hdwallet-provider')
import HDWalletProvider from '@truffle/hdwallet-provider'
const configJson: Config = {
nodeUri: 'http://localhost:8545',
aquariusUri: 'http://172.15.0.15:5000',
aquariusUri: 'http://aquarius:5000',
brizoUri: 'http://localhost:8030',
secretStoreUri: 'http://localhost:12001',
brizoAddress: '0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0',
@ -44,12 +44,7 @@ if (process.env.SEED_WORDS) {
const seedphrase = process.env.SEED_WORDS
// @ts-ignore
configJson.web3Provider = new HDWalletProvider(
seedphrase,
configJson.nodeUri,
0,
5
)
configJson.web3Provider = new HDWalletProvider(seedphrase, configJson.nodeUri, 0, 5)
}
export const config: Config & { forceVerbose: Config } = configJson as any

View File

@ -40,19 +40,33 @@ describe('Asset Owners', () => {
assert.isTrue(isProvider)
})
it('should get the assets owned by a user', async () => {
const { length: initialLength } = await ocean.assets.ownerAssets(
account2.getId()
it('should be added correctly a permission on an asset', async () => {
const ddo = await ocean.assets.create(metadata as any, account1)
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, account2)
const { length: finalLength } = await ocean.assets.ownerAssets(
account2.getId()
)
const { length: finalLength } = await ocean.assets.ownerAssets(account2.getId())
assert.equal(finalLength - initialLength, 1)
})
@ -72,16 +86,13 @@ describe('Asset Owners', () => {
// Granting access
try {
await account2.requestTokens(
+metadata.base.price *
10 ** -(await ocean.keeper.token.decimals())
+metadata.main.price * 10 ** -(await ocean.keeper.token.decimals())
)
} catch {}
await ocean.assets.order(
ddo.id,
ddo.findServiceByType('Access').serviceDefinitionId,
account2
)
const { index } = ddo.findServiceByType('access')
await ocean.assets.order(ddo.id, index, account2)
// Access granted
const { length: finalLength2 } = await ocean.assets.consumerAssets(
@ -89,4 +100,14 @@ describe('Asset Owners', () => {
)
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()
})
after(async () => {
try {
localStorage.clear()
} catch {}
})
it('should generate a token', async () => {
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)
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.isAtLeast(
ddo.authentication.length,
1,
'Default authentication not added'
)
assert.isAtLeast(ddo.authentication.length, 1, 'Default authentication not added')
assert.isDefined(
ddo.findServiceByType('Access'),
"DDO Access service doesn't exist"
ddo.findServiceByType('access'),
"DDO access service doesn't exist"
)
})
it('should be able to request tokens for consumer', async () => {
const initialBalance = (await consumer.getBalance()).ocn
const claimedTokens =
+metadata.base.price * 10 ** -(await ocean.keeper.token.decimals())
+metadata.main.price * 10 ** -(await ocean.keeper.token.decimals())
try {
await consumer.requestTokens(claimedTokens)
@ -64,11 +60,11 @@ describe('Consume Asset', () => {
})
it('should sign the service agreement', async () => {
const accessService = ddo.findServiceByType('Access')
const accessService = ddo.findServiceByType('access')
serviceAgreementSignatureResult = await ocean.agreements.prepare(
ddo.id,
accessService.serviceDefinitionId,
accessService.index,
consumer
)
@ -86,12 +82,12 @@ describe('Consume Asset', () => {
})
it('should execute the service agreement', async () => {
const accessService = ddo.findServiceByType('Access')
const accessService = ddo.findServiceByType('access')
const success = await ocean.agreements.create(
ddo.id,
serviceAgreementSignatureResult.agreementId,
accessService.serviceDefinitionId,
accessService.index,
serviceAgreementSignatureResult.signature,
consumer,
publisher
@ -115,7 +111,7 @@ describe('Consume Asset', () => {
it('should lock the payment by the consumer', async () => {
const paid = await ocean.agreements.conditions.lockReward(
serviceAgreementSignatureResult.agreementId,
ddo.findServiceByType('Metadata').metadata.base.price,
ddo.findServiceByType('metadata').attributes.main.price,
consumer
)
@ -156,13 +152,13 @@ describe('Consume Asset', () => {
})
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 path = await ocean.assets.consume(
serviceAgreementSignatureResult.agreementId,
ddo.id,
accessService.serviceDefinitionId,
accessService.index,
consumer,
folder
)
@ -182,14 +178,14 @@ describe('Consume Asset', () => {
)
})
it('should consume and store one assets', async () => {
const accessService = ddo.findServiceByType('Access')
it('should consume and store one asset', async () => {
const accessService = ddo.findServiceByType('access')
const folder = '/tmp/ocean/squid-js-2'
const path = await ocean.assets.consume(
serviceAgreementSignatureResult.agreementId,
ddo.id,
accessService.serviceDefinitionId,
accessService.index,
consumer,
folder,
1

View File

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

View File

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

View File

@ -4,11 +4,7 @@ import { config } from '../config'
import { Ocean, templates, conditions, utils, Account, Keeper } from '../../src' // @oceanprotocol/squid
const {
LockRewardCondition,
EscrowReward,
AccessSecretStoreCondition
} = conditions
const { LockRewardCondition, EscrowReward, AccessSecretStoreCondition } = conditions
describe('Register Escrow Access Secret Store Template', () => {
let ocean: Ocean
@ -40,8 +36,7 @@ describe('Register Escrow Access Secret Store Template', () => {
consumer = (await ocean.accounts.list())[2]
// Conditions
accessSecretStoreCondition =
keeper.conditions.accessSecretStoreCondition
accessSecretStoreCondition = keeper.conditions.accessSecretStoreCondition
lockRewardCondition = keeper.conditions.lockRewardCondition
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')
test1length = ddos.length
test2length = (await ocean.assets.search(`Test2${testHash}`)).results
.length
test3length = (await ocean.assets.search(`Test3${testHash}`)).results
.length
test2length = (await ocean.assets.search(`Test2${testHash}`)).results.length
test3length = (await ocean.assets.search(`Test3${testHash}`)).results.length
if (!ocean.keeper.dispenser) {
price = 0
}
})
it('should regiester some a asset', async () => {
it('should register an asset', async () => {
assert.instanceOf(
await ocean.assets.create(
metadataGenerator('Test1') as any,
publisher
),
await ocean.assets.create(metadataGenerator('Test1') as any, publisher),
DDO
)
assert.instanceOf(
await ocean.assets.create(
metadataGenerator('Test2') as any,
publisher
),
await ocean.assets.create(metadataGenerator('Test2') as any, publisher),
DDO
)
assert.instanceOf(
await ocean.assets.create(
metadataGenerator('Test2') as any,
publisher
),
await ocean.assets.create(metadataGenerator('Test2') as any, publisher),
DDO
)
assert.instanceOf(
await ocean.assets.create(
metadataGenerator('Test3') as any,
publisher
),
await ocean.assets.create(metadataGenerator('Test3') as any, publisher),
DDO
)
})
it('should search by text and see the increment of DDOs', async () => {
assert.equal(
(await ocean.assets.search(`Test2${testHash}`)).results.length -
test2length,
(await ocean.assets.search(`Test2${testHash}`)).results.length - test2length,
2,
'Something was wrong searching the assets'
)
assert.equal(
(await ocean.assets.search(`Test3${testHash}`)).results.length -
test3length,
(await ocean.assets.search(`Test3${testHash}`)).results.length - test3length,
1,
'Something was wrong searching the assets'
)

View File

@ -21,11 +21,7 @@ describe('Secret Store', () => {
})
it('should encrypt a text', async () => {
encryptedContent = await ocean.secretStore.encrypt(
did.getId(),
content,
account
)
encryptedContent = await ocean.secretStore.encrypt(did.getId(), content, account)
assert.isDefined(encryptedContent)
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 templateId = `0x${'f'.repeat(40)}`
const agreementId = `0x${'e'.repeat(64)}`
const serviceDefinitionId = '0'
const serviceAgreementTemplate = await templates.escrowAccessSecretStoreTemplate.getServiceAgreementTemplate()
@ -54,17 +53,20 @@ describe('Signature', () => {
id: did,
service: [
{
type: 'Access',
type: 'access',
index: 0,
purchaseEndpoint: undefined,
serviceEndpoint: undefined,
serviceDefinitionId,
templateId,
attributes: {
serviceAgreementTemplate
}
} as any,
{
type: 'Metadata',
metadata: {
base: {
type: 'metadata',
index: 1,
attributes: {
main: {
price: 10
}
}
@ -74,13 +76,9 @@ describe('Signature', () => {
const signature = await ocean.utils.agreements.signServiceAgreement(
ddo,
serviceDefinitionId,
0,
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
)

View File

@ -11,7 +11,8 @@ describe('Versions', () => {
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()
assert.equal(versions.aquarius.status, OceanPlatformTechStatus.Working)

View File

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

View File

@ -1,18 +1,33 @@
import { MetaData } from '../../src' // @oceanprotocol/squid
const metadata: Partial<MetaData> = {
base: {
main: {
name: undefined,
type: 'dataset',
description:
'Weather information of UK including temperature and humidity',
dateCreated: '2012-10-10T17:00:00Z',
datePublished: '2012-10-10T17:00:00Z',
author: 'Met Office',
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',
workExample:
'423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68',
workExample: '423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68',
links: [
{
name: 'Sample of Asset Data',
@ -27,34 +42,20 @@ const metadata: Partial<MetaData> = {
],
inLanguage: 'en',
categories: ['Economy', 'Data Science'],
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'
}
]
tags: ['weather', 'uk', '2011', 'temperature', 'humidity']
}
}
export const generateMetadata = (
name: string,
price?: number
): Partial<MetaData> => ({
export const generateMetadata = (name: string, price?: number): Partial<MetaData> => ({
...metadata,
base: {
...metadata.base,
main: {
...metadata.main,
name,
price: (price || 21) + '0'.repeat(18)
},
additionalInformation: {
...metadata.additionalInformation
}
})
export const getMetadata = (price?: number) =>
generateMetadata('TestAsset', price)
export const getMetadata = (price?: number) => generateMetadata('TestAsset', price)

View File

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

6164
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -11,7 +11,7 @@ mkdir -p artifacts
until [ $COMMAND_STATUS -eq 0 ] || [ $RETRY_COUNT -eq 120 ]; do
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=$?
sleep 5
(( RETRY_COUNT=RETRY_COUNT+1 ))

View File

@ -15,10 +15,7 @@ const outPath = './dist/squid-js.json'
const files = ['./src/squid.ts']
// specifically point to tsconfig, otherwise TypeDoc fails
const config = typescript.findConfigFile(
'./tsconfig.js',
typescript.sys.fileExists
)
const config = typescript.findConfigFile('./tsconfig.js', typescript.sys.fileExists)
const generateJson = () => {
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 { Logger, LoggerInstance, LogLevel } from './utils'
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(config: InstantiableConfig): Promise<any> {
LoggerInstance.warn(
'getInstance() methods has needs to be added to child class.'
)
LoggerInstance.warn('getInstance() methods has needs to be added to child class.')
}
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> {
const accessUrl: string = await this.ocean.utils.fetch
.post(
`${accessToken.service_endpoint}/${accessToken.resource_id}`,
payload
)
.post(`${accessToken.service_endpoint}/${accessToken.resource_id}`, payload)
.then((response: any): string => {
if (response.ok) {
return response.text()
}
this.logger.error(
'Failed: ',
response.status,
response.statusText
)
this.logger.error('Failed: ', response.status, response.statusText)
return null
})
.then((consumptionUrl: string): string => {
this.logger.error(
'Success accessing consume endpoint: ',
consumptionUrl
)
this.logger.error('Success accessing consume endpoint: ', consumptionUrl)
return consumptionUrl
})
.catch(error => {
@ -134,10 +124,7 @@ export class Aquarius extends Instantiable {
return this.transformResult(results)
})
.catch(error => {
this.logger.error(
'Error fetching querying metadata by text: ',
error
)
this.logger.error('Error fetching querying metadata by text: ', error)
return this.transformResult()
})
@ -186,8 +173,7 @@ export class Aquarius extends Instantiable {
metadataServiceEndpoint?: string
): Promise<DDO> {
did = did && DID.parse(did)
const fullUrl =
metadataServiceEndpoint || `${this.url}${apiPath}/${did.getDid()}`
const fullUrl = metadataServiceEndpoint || `${this.url}${apiPath}/${did.getDid()}`
const result = await this.ocean.utils.fetch
.get(fullUrl)
.then((response: any) => {
@ -222,12 +208,7 @@ export class Aquarius extends Instantiable {
}
private transformResult(
{
results,
page,
total_pages: totalPages,
total_results: totalResults
}: any = {
{ results, page, total_pages: totalPages, total_results: totalResults }: any = {
result: [],
page: 0,
total_pages: 0, // eslint-disable-line @typescript-eslint/camelcase

View File

@ -37,7 +37,7 @@ export class Brizo extends Instantiable {
public getComputeEndpoint(
pubKey: string,
serviceId: string,
serviceIndex: number,
_notUsed: string,
container: string
) {
@ -47,14 +47,14 @@ export class Brizo extends Instantiable {
public async initializeServiceAgreement(
did: string,
serviceAgreementId: string,
serviceDefinitionId: string,
serviceIndex: number,
signature: string,
consumerAddress: string
): Promise<any> {
const args = {
did,
serviceAgreementId,
serviceDefinitionId,
serviceIndex,
signature,
consumerAddress
}
@ -94,11 +94,7 @@ export class Brizo extends Instantiable {
consumeUrl += `&signature=${signature}`
try {
await this.ocean.utils.fetch.downloadFile(
consumeUrl,
destination,
i
)
await this.ocean.utils.fetch.downloadFile(consumeUrl, destination, i)
} catch (e) {
this.logger.error('Error consuming assets')
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 LoggerInstance from '../utils/Logger'
import { Ocean } from '../ocean/Ocean'
import { Authentication } from './Authentication'
import { Proof } from './Proof'
@ -53,8 +51,7 @@ export class DDO {
public constructor(ddo: Partial<DDO> = {}) {
Object.assign(this, ddo, {
created:
(ddo && ddo.created) ||
new Date().toISOString().replace(/\.[0-9]{3}/, '')
(ddo && ddo.created) || new Date().toISOString().replace(/\.[0-9]{3}/, '')
})
}
@ -63,20 +60,16 @@ export class DDO {
}
/**
* Finds a service of a DDO by ID.
* @param {string} serviceDefinitionId Service ID.
* Finds a service of a DDO by index.
* @param {number} Service index.
* @return {Service} Service.
*/
public findServiceById<T extends ServiceType>(
serviceDefinitionId: string
): Service<T> {
if (!serviceDefinitionId) {
throw new Error('serviceDefinitionId not set')
public findServiceById<T extends ServiceType>(index: number): Service<T> {
if (isNaN(index)) {
throw new Error('index is not set')
}
const service = this.service.find(
s => s.serviceDefinitionId === serviceDefinitionId
)
const service = this.service.find(s => s.index === index)
return service as Service<T>
}
@ -86,9 +79,7 @@ export class DDO {
* @param {string} serviceType Service type.
* @return {Service} Service.
*/
public findServiceByType<T extends ServiceType>(
serviceType: T
): Service<T> {
public findServiceByType<T extends ServiceType>(serviceType: T): Service<T> {
if (!serviceType) {
throw new Error('serviceType not set')
}
@ -101,8 +92,8 @@ export class DDO {
* @return {string[]} DDO checksum.
*/
public getChecksum(): string {
const { metadata } = this.findServiceByType('Metadata')
const { files, name, author, license } = metadata.base
const { attributes } = this.findServiceByType('metadata')
const { files, name, author, license } = attributes.main
const values = [
...(files || []).map(({ checksum }) => checksum).filter(_ => !!_),
@ -119,9 +110,9 @@ export class DDO {
/**
* 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} password Password if it's requirted.
* @param {string} password Password if it's required.
* @return {Promise<Proof>} Proof object.
*/
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.
* @param {Web3} web3 Web3 instance.
* @param {Ocean} ocean Ocean instance.
* @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.
*/
public async addProof(
web3: Web3,
ocean: Ocean,
publicKey: string,
password?: string
): Promise<void> {
if (this.proof) {
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 {
/**
* File name.
@ -17,6 +86,13 @@ export interface File {
*/
index?: number
/**
* File format, if applicable.
* @type {string}
* @example "text/csv"
*/
contentType: string
/**
* File checksum.
* @type {[type]}
@ -33,7 +109,7 @@ export interface File {
* File content length.
* @type {[type]}
*/
contentLength?: number
contentLength?: string
/**
* Resource ID (depending on the source).
@ -54,20 +130,13 @@ export interface File {
* @example "zip"
*/
compression?: string
/**
* File format, if applicable.
* @type {string}
* @example "text/csv"
*/
contentType?: string
}
/**
* Base attributes of Assets Metadata.
* @see https://github.com/oceanprotocol/OEPs/tree/master/8#base-attributes
* Main attributes of assets metadata.
* @see https://github.com/oceanprotocol/OEPs/tree/master/8
*/
export interface MetaDataBase {
export interface MetaDataMain {
/**
* Descriptive name of the Asset.
* @type {string}
@ -83,14 +152,6 @@ export interface MetaDataBase {
*/
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
* ISO 8601 format, Coordinated Universal Time.
@ -123,6 +184,75 @@ export interface MetaDataBase {
*/
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.
* @type {string}
@ -179,60 +309,6 @@ export interface MetaDataBase {
*/
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,
* annually, quarterly, etc.), or is the resource static that is never expected
@ -240,28 +316,22 @@ export interface AdditionalInformation {
* @type {string}
* @example "yearly"
*/
updateFrequency: string
updateFrequency?: string
/**
* A link to machine-readable structured markup (such as ttl/json-ld/rdf)
* describing the dataset.
* @type {StructuredMarkup[]}
*/
structuredMarkup: {
structuredMarkup?: {
uri: 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 {
main: MetaDataMain
encryptedFiles?: string
additionalInformation?: AdditionalInformation
base: MetaDataBase
curation?: Curation
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@ export abstract class ContractBase extends Instantiable {
return this.contract.options.address
}
constructor(contractName, private optional: boolean = false) {
constructor(contractName: string, private optional: boolean = false) {
super()
this.contractName = contractName
}
@ -54,10 +54,7 @@ export abstract class ContractBase extends Instantiable {
protected async init(config: InstantiableConfig) {
this.setInstanceConfig(config)
const contractHandler = new ContractHandler(config)
this.contract = await contractHandler.get(
this.contractName,
this.optional
)
this.contract = await contractHandler.get(this.contractName, this.optional)
}
protected async getFromAddress(from?: string): Promise<string> {
@ -99,23 +96,19 @@ export abstract class ContractBase extends Instantiable {
})
return tx
} catch (err) {
const mappedArgs = this.searchMethod(name, args).inputs.map(
(input, i) => {
const mappedArgs = this.searchMethod(name, args).inputs.map((input, i) => {
return {
name: input.name,
value: args[i]
}
}
)
})
this.logger.error('-'.repeat(40))
this.logger.error(
`Sending transaction "${name}" on contract "${this.contractName}" failed.`
)
this.logger.error(`Error: ${err.message}`)
this.logger.error(`From: ${from}`)
this.logger.error(
`Parameters: ${JSON.stringify(mappedArgs, null, 2)}`
)
this.logger.error(`Parameters: ${JSON.stringify(mappedArgs, null, 2)}`)
this.logger.error('-'.repeat(40))
throw err
}
@ -127,9 +120,7 @@ export abstract class ContractBase extends Instantiable {
from?: string
): Promise<T> {
if (!this.contract.methods[name]) {
throw new Error(
`Method ${name} is not part of contract ${this.contractName}`
)
throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
}
// Logger.log(name)
try {
@ -150,11 +141,7 @@ export abstract class ContractBase extends Instantiable {
`Event ${eventName} is not part of contract ${this.contractName}`
)
}
return this.ocean.keeper.utils.eventHandler.getEvent(
this,
eventName,
filter
)
return this.ocean.keeper.utils.eventHandler.getEvent(this, eventName, filter)
}
private searchMethod(methodName: string, args: any[] = []) {
@ -165,8 +152,7 @@ export abstract class ContractBase extends Instantiable {
}))
.filter((method: any) => method.name === methodName)
const foundMethod =
methods.find(({ inputs }) => inputs.length === args.length) ||
methods[0]
methods.find(({ inputs }) => inputs.length === args.length) || methods[0]
if (!foundMethod) {
throw new Error(
`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 { zeroX, didPrefixed, didZeroX } from '../../utils'
import { zeroX, noZeroX, didPrefixed, didZeroX } from '../../utils'
import { InstantiableConfig } from '../../Instantiable.abstract'
export default class DIDRegistry extends ContractBase {
public static async getInstance(
config: InstantiableConfig
): Promise<DIDRegistry> {
public static async getInstance(config: InstantiableConfig): Promise<DIDRegistry> {
const didRegistry: DIDRegistry = new DIDRegistry('DIDRegistry')
await didRegistry.init(config)
return didRegistry
@ -39,9 +38,11 @@ export default class DIDRegistry extends ContractBase {
}
public async getAttributesByOwner(owner: string): Promise<string[]> {
return (await this.getPastEvents('DIDAttributeRegistered', {
return (
await this.getPastEvents('DIDAttributeRegistered', {
_owner: zeroX(owner)
}))
})
)
.map(({ returnValues }) => returnValues._did)
.map(didPrefixed)
}
@ -49,15 +50,13 @@ export default class DIDRegistry extends ContractBase {
public async getAttributesByDid(
did: string
): Promise<{ did: string; serviceEndpoint: string; checksum: string }> {
return (await this.getPastEvents('DIDAttributeRegistered', {
return (
await this.getPastEvents('DIDAttributeRegistered', {
_did: didZeroX(did)
})).map(
})
).map(
({
returnValues: {
_did,
_checksum: checksum,
_value: serviceEndpoint
}
returnValues: { _did, _checksum: checksum, _value: serviceEndpoint }
}) => ({
did: didPrefixed(_did),
serviceEndpoint,
@ -65,4 +64,27 @@ export default class DIDRegistry extends ContractBase {
})
)[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'
export default class Dispenser extends ContractBase {
public static async getInstance(
config: InstantiableConfig
): Promise<Dispenser> {
public static async getInstance(config: InstantiableConfig): Promise<Dispenser> {
const dispenser: Dispenser = new Dispenser('Dispenser', true)
await dispenser.init(config)
return dispenser
}
public async requestTokens(
amount: number | string,
receiverAddress: string
) {
public async requestTokens(amount: number | string, receiverAddress: string) {
return this.send('requestTokens', receiverAddress, [String(amount)])
}
}

View File

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

View File

@ -17,17 +17,8 @@ export class AccessSecretStoreCondition extends Condition {
return super.hashValues(didZeroX(did), zeroX(grantee))
}
public fulfill(
agreementId: string,
did: string,
grantee: string,
from?: string
) {
return super.fulfill(
agreementId,
[didZeroX(did), grantee].map(zeroX),
from
)
public fulfill(agreementId: string, did: string, grantee: string, from?: string) {
return super.fulfill(agreementId, [didZeroX(did), grantee].map(zeroX), from)
}
public checkPermissions(grantee: string, did: string, from?: string) {
@ -41,9 +32,11 @@ export class AccessSecretStoreCondition extends Condition {
public async getGrantedDidByConsumer(
consumer: string
): Promise<{ did: string; agreementId: string }[]> {
return (await this.getPastEvents('Fulfilled', {
return (
await this.getPastEvents('Fulfilled', {
_grantee: zeroX(consumer)
})).map(({ returnValues }) => ({
})
).map(({ returnValues }) => ({
did: didPrefixed(returnValues._documentId),
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'
export class EscrowReward extends Condition {
public static async getInstance(
config: InstantiableConfig
): Promise<EscrowReward> {
public static async getInstance(config: InstantiableConfig): Promise<EscrowReward> {
return Condition.getInstance(config, 'EscrowReward', EscrowReward)
}
@ -33,12 +31,7 @@ export class EscrowReward extends Condition {
) {
return super.fulfill(
agreementId,
[
amount,
...[receiver, sender, lockCondition, releaseCondition].map(
zeroX
)
],
[amount, ...[receiver, sender, lockCondition, releaseCondition].map(zeroX)],
from
)
}

View File

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

View File

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

View File

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

View File

@ -25,9 +25,7 @@ export abstract class AgreementTemplate extends ContractBase {
conditionName: string,
templateClass: any
): Promise<AgreementTemplate & any> {
const condition: AgreementTemplate = new (templateClass as any)(
conditionName
)
const condition: AgreementTemplate = new (templateClass as any)(conditionName)
await condition.init(config)
return condition
}
@ -114,9 +112,7 @@ export abstract class AgreementTemplate extends ContractBase {
from?: string
): Promise<boolean>
public abstract async getServiceAgreementTemplate(): Promise<
ServiceAgreementTemplate
>
public abstract async getServiceAgreementTemplate(): Promise<ServiceAgreementTemplate>
public async getServiceAgreementTemplateConditions() {
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 {
contractName
} = await this.getServiceAgreementTemplateConditionByRef(ref)
const { contractName } = await this.getServiceAgreementTemplateConditionByRef(
ref
)
return {
ref,
contractName,
state: (await conditionStore.getCondition(
state: (
await conditionStore.getCondition(
conditionIdByConddition[contractName]
)).state
)
).state
}
})
const states = await Promise.all(statesPromises)
@ -181,9 +179,7 @@ export abstract class AgreementTemplate extends ContractBase {
return states.reduce((acc, { contractName, ref, state }) => {
const blockers = dependencies[ref]
.map(dependency => states.find(_ => _.ref === dependency))
.filter(
condition => condition.state !== ConditionState.Fulfilled
)
.filter(condition => condition.state !== ConditionState.Fulfilled)
return {
...acc,
[ref]: {
@ -217,11 +213,7 @@ export abstract class AgreementTemplate extends ContractBase {
this.logger.bypass('-'.repeat(20))
}
this.logger.bypass(`${condition} (${contractName})`)
this.logger.bypass(
' Status:',
state,
`(${conditionStateNames[state]})`
)
this.logger.bypass(' Status:', state, `(${conditionStateNames[state]})`)
if (blocked) {
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 { BaseEscrowTemplate } from './BaseEscrowTemplate.abstract'
import { DDO } from '../../../ddo/DDO'
import { generateId, zeroX } from '../../../utils'
import { InstantiableConfig } from '../../../Instantiable.abstract'
import { escrowAccessSecretStoreTemplateServiceAgreementTemplate } from './EscrowAccessSecretStoreTemplate.serviceAgreementTemplate'
export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
export class EscrowAccessSecretStoreTemplate extends BaseEscrowTemplate {
public static async getInstance(
config: InstantiableConfig
): Promise<EscrowAccessSecretStoreTemplate> {
@ -20,37 +21,6 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
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(
agreementId: string,
ddo: DDO,
@ -59,7 +29,7 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
) {
return !!(await this.createFullAgreement(
ddo.shortId(),
ddo.findServiceByType('Metadata').metadata.base.price,
ddo.findServiceByType('metadata').attributes.main.price,
consumer,
from,
agreementId
@ -79,14 +49,10 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
} = await this.createFullAgreementData(
agreementId,
ddo.shortId(),
ddo.findServiceByType('Metadata').metadata.base.price,
ddo.findServiceByType('metadata').attributes.main.price,
consumer
)
return [
accessSecretStoreConditionId,
lockRewardConditionId,
escrowRewardId
]
return [accessSecretStoreConditionId, lockRewardConditionId, escrowRewardId]
}
/**
@ -107,21 +73,12 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
accessSecretStoreConditionId,
lockRewardConditionId,
escrowRewardId
} = await this.createFullAgreementData(
agreementId,
did,
amount,
consumer
)
} = await this.createFullAgreementData(agreementId, did, amount, consumer)
await this.createAgreement(
agreementId,
did,
[
accessSecretStoreConditionId,
lockRewardConditionId,
escrowRewardId
],
[accessSecretStoreConditionId, lockRewardConditionId, escrowRewardId],
[0, 0, 0],
[0, 0, 0],
consumer,
@ -172,8 +129,4 @@ export class EscrowAccessSecretStoreTemplate extends AgreementTemplate {
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 {
EscrowAccessSecretStoreTemplate
} from './EscrowAccessSecretStoreTemplate'
export { BaseEscrowTemplate } from './BaseEscrowTemplate.abstract'
export { 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.assets = await OceanAssets.getInstance(instanceConfig)
instance.agreements = await OceanAgreements.getInstance(instanceConfig)
instance.secretStore = await OceanSecretStore.getInstance(
instanceConfig
)
instance.secretStore = await OceanSecretStore.getInstance(instanceConfig)
instance.tokens = await OceanTokens.getInstance(instanceConfig)
instance.versions = await OceanVersions.getInstance(instanceConfig)

View File

@ -10,9 +10,7 @@ export class OceanAccounts extends Instantiable {
* Returns the instance of OceanAccounts.
* @return {Promise<OceanAccounts>}
*/
public static async getInstance(
config: InstantiableConfig
): Promise<OceanAccounts> {
public static async getInstance(config: InstantiableConfig): Promise<OceanAccounts> {
const instance = new OceanAccounts()
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 {number} amount Token amount.
* @return {Promise<boolean>} Success.
*/
public async requestTokens(
account: Account,
amount: number
): Promise<boolean> {
public async requestTokens(account: Account, amount: number): Promise<boolean> {
try {
await account.requestTokens(amount)
return true

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,9 +12,7 @@ export class SubscribablePromise<T extends any, P extends any> {
this
)
constructor(
executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>
) {
constructor(executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>) {
// Defear
setTimeout(() => this.init(executor), 1)
}
@ -28,10 +26,7 @@ export class SubscribablePromise<T extends any, P extends any> {
return this
}
public then(
onfulfilled?: (value: P) => any,
onrejected?: (error: any) => any
) {
public then(onfulfilled?: (value: P) => any, onrejected?: (error: any) => any) {
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)
}
private init(
executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>
) {
private init(executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>) {
const execution = executor(this.observer)
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'
describe('Squid', () => {

View File

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

View File

@ -1,6 +1,6 @@
import { assert, expect, spy, use } from 'chai'
import * as spies from 'chai-spies'
import * as Web3 from 'web3'
import spies from 'chai-spies'
import Web3 from 'web3'
import { DDO } from '../../src/ddo/DDO'
import { Service } from '../../src/ddo/Service'
@ -46,8 +46,7 @@ describe('DDO', () => {
},
{
type: 'CredentialRepositoryService',
serviceEndpoint:
'https://repository.example.com/service/8377464'
serviceEndpoint: 'https://repository.example.com/service/8377464'
},
{
type: 'XdiService',
@ -77,29 +76,58 @@ describe('DDO', () => {
serviceEndpoint: 'https://bops.example.com/enterprise/'
},
{
type: 'Consume',
type: 'consume',
serviceEndpoint:
'http://mybrizo.org/api/v1/brizo/services/consume?pubKey={pubKey}&serviceId={serviceId}&url={url}'
},
{
type: 'Compute',
type: 'compute',
serviceEndpoint:
'http://mybrizo.org/api/v1/brizo/services/compute?pubKey={pubKey}&serviceId={serviceId}&algo={algo}&container={container}'
},
{
type: 'Metadata',
type: 'metadata',
index: 0,
serviceEndpoint:
'http://myaquarius.org/api/v1/provider/assets/metadata/{did}',
metadata: {
base: {
attributes: {
main: {
name: 'UK Weather information 2011',
type: 'dataset',
description:
'Weather information of UK including temperature and humidity',
dateCreated: '2012-10-10T17:00:000Z',
datePublished: '2012-10-10T17:00:000Z',
author: 'Met Office',
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',
workExample:
'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/'
},
{
fieldsDescription:
'http://data.ceda.ac.uk/badc/ukcp09/'
fieldsDescription: 'http://data.ceda.ac.uk/badc/ukcp09/'
}
],
inLanguage: 'en',
categories: ['Economy', 'Data Science'],
tags: [
'weather',
'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',
tags: ['weather', 'uk', '2011', 'temperature', 'humidity'],
updateFrequency: 'yearly',
structuredMarkup: [
{
uri:
'http://skos.um.es/unescothes/C01194/jsonld',
uri: 'http://skos.um.es/unescothes/C01194/jsonld',
mediaType: 'application/ld+json'
},
{
uri:
'http://skos.um.es/unescothes/C01194/turtle',
uri: 'http://skos.um.es/unescothes/C01194/turtle',
mediaType: 'text/turtle'
}
]
@ -230,10 +223,7 @@ describe('DDO', () => {
assert.instanceOf(ddo, DDO)
assert.equal(ddo.id, testDDO.id)
assert.equal(
ddo.publicKey[0].publicKeyPem,
testDDO.publicKey[0].publicKeyPem
)
assert.equal(ddo.publicKey[0].publicKeyPem, testDDO.publicKey[0].publicKeyPem)
})
it('should properly deserialize from json file', async () => {
@ -241,10 +231,7 @@ describe('DDO', () => {
assert(ddo)
assert.equal(ddo.id, jsonDDO.id)
assert.equal(
ddo.publicKey[0].publicKeyPem,
jsonDDO.publicKey[0].publicKeyPem
)
assert.equal(ddo.publicKey[0].publicKeyPem, jsonDDO.publicKey[0].publicKeyPem)
})
})
@ -265,11 +252,7 @@ describe('DDO', () => {
const signature = `0x${'a'.repeat(130)}`
it('should properly generate the proof', async () => {
const signTextSpy = spy.on(
ocean.utils.signature,
'signText',
() => signature
)
const signTextSpy = spy.on(ocean.utils.signature, 'signText', () => signature)
const ddo = new DDO(testDDO)
const checksum = ddo.getChecksum()
const proof = await ddo.generateProof(ocean, publicKey)
@ -294,12 +277,8 @@ describe('DDO', () => {
signaturValue: 'test'
} as any
const ddo = new DDO(testDDO)
const generateProofSpy = spy.on(
ddo,
'generateProof',
() => fakeProof
)
await ddo.addProof(web3, publicKey)
const generateProofSpy = spy.on(ddo, 'generateProof', () => fakeProof)
await ddo.addProof(ocean, publicKey)
assert.equal(ddo.proof, fakeProof)
expect(generateProofSpy).to.have.been.called.with(publicKey)

View File

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

View File

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

View File

@ -13,7 +13,7 @@ describe('DIDRegistry', () => {
before(async () => {
await TestContractHandler.prepareContracts()
ocean = await Ocean.getInstance(config)
didRegistry = ocean.keeper.didRegistry
;({ didRegistry } = ocean.keeper)
})
describe('#registerAttribute()', () => {
@ -89,4 +89,36 @@ describe('DIDRegistry', () => {
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 * as spies from 'chai-spies'
import spies from 'chai-spies'
import { EventHandler } from '../../src/keeper/EventHandler'
import { Ocean } from '../../src/ocean/Ocean'
import config from '../config'
@ -27,11 +27,7 @@ describe('EventHandler', () => {
assert.isDefined(subscription)
const countAfter = eventHandler.count
assert.equal(
countBefore + 1,
countAfter,
'The event seems not added.'
)
assert.equal(countBefore + 1, countAfter, 'The event seems not added.')
subscription.unsubscribe()
})
@ -58,11 +54,7 @@ describe('EventHandler', () => {
eventHandler.unsubscribe(callback)
const countAfter = eventHandler.count
assert.equal(
countBefore,
countAfter,
'The event seems not removed.'
)
assert.equal(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 Web3Provider from '../../src/keeper/Web3Provider'
import Logger from '../../src/utils/Logger'
import config from '../config'
interface ContractTest extends Contract {
testContract?: boolean
$initialized?: boolean
}
export default class TestContractHandler extends ContractHandler {
public static async prepareContracts() {
const web3 = Web3Provider.getWeb3(config)
@ -134,32 +139,30 @@ export default class TestContractHandler extends ContractHandler {
from: string,
args: any[] = [],
tokens: { [name: string]: string } = {}
): Promise<Contract & { $initialized: boolean }> {
): Promise<ContractTest> {
const where = this.networkId
// dont redeploy if there is already something loaded
if (TestContractHandler.hasContract(name, where)) {
const contract = await ContractHandler.getContract(name, where)
const contract: ContractTest = await ContractHandler.getContract(name, where)
if (contract.testContract) {
return { ...contract, $initialized: true }
return { ...contract, $initialized: true } as any
}
}
const web3 = Web3Provider.getWeb3(config)
let contractInstance: Contract
let contractInstance: ContractTest
try {
Logger.log('Deploying', name)
const sendConfig = {
from,
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 tempContract = new web3.eth.Contract(
artifact.abi,
artifact.address
)
const tempContract = new web3.eth.Contract(artifact.abi, artifact.address)
const isZos = !!tempContract.methods.initialize
Logger.debug({
@ -184,13 +187,11 @@ export default class TestContractHandler extends ContractHandler {
})
.send(sendConfig)
if (isZos) {
await contractInstance.methods
.initialize(...args)
.send(sendConfig)
await contractInstance.methods.initialize(...args).send(sendConfig)
}
contractInstance.testContract = true
ContractHandler.setContract(name, where, contractInstance)
// Logger.log("Deployed", name, "at", contractInstance.options.address);
// Logger.log('Deployed', name, 'at', contractInstance.options.address)
} catch (err) {
Logger.error(
'Deployment failed for',

View File

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

View File

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

View File

@ -1,7 +1,6 @@
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 { Ocean } from '../../src/ocean/Ocean'
import config from '../config'
@ -41,33 +40,4 @@ describe('Ocean', () => {
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 * as spies from 'chai-spies'
import spies from 'chai-spies'
import config from '../config'
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 * as spies from 'chai-spies'
import spies from 'chai-spies'
import config from '../config'
import Account from '../../src/ocean/Account'
@ -54,11 +54,7 @@ describe('OceanAuth', () => {
describe('#store()', () => {
it('should sign and store the token', async () => {
const writeTokenSpy = spy.on(
oceanAuth as any,
'writeToken',
() => {}
)
const writeTokenSpy = spy.on(oceanAuth as any, 'writeToken', () => null)
await oceanAuth.store(account)

View File

@ -1,5 +1,5 @@
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 { Ocean } from '../../src/ocean/Ocean'
@ -33,11 +33,7 @@ describe('OceanSecretStore', () => {
() => 'encryptedResult'
)
const result = await oceanSecretStore.encrypt(
did,
'test',
accounts[0]
)
const result = await oceanSecretStore.encrypt(did, 'test', accounts[0])
expect(secretStoreEncryptSpy).to.have.been.called.with(did, 'test')

View File

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

View File

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

View File

@ -2,6 +2,8 @@
"compilerOptions": {
"resolveJsonModule": true,
"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 * as spies from 'chai-spies'
import spies from 'chai-spies'
import { SubscribableObserver } from '../../src/utils/SubscribableObserver'

View File

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

View File

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