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

Merge pull request #45 from oceanprotocol/feature/sa_register

Feature/sa register
This commit is contained in:
Sebastian Gerske 2018-11-21 17:18:56 +01:00 committed by GitHub
commit 484ec08d61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 3849 additions and 797 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.7
current_version = 0.1.21
[bumpversion:file:package.json]

View File

@ -14,7 +14,7 @@ matrix:
before_install:
- npm install -g npm
- npm install -g release-it greenkeeper-lockfile ganache-cli
- npm install -g release-it greenkeeper-lockfile ganache-cli@~6.1.8
before_script:
- greenkeeper-lockfile-update

View File

@ -21,6 +21,7 @@
## Table of Contents
- [Get started](#get-started)
- [Examples](#examples)
- [Development](#development)
- [Production build](#production-build)
- [npm releases](#npm-releases)
@ -49,12 +50,31 @@ const { Ocean, Logger } = require('@oceanprotocol/squid')
You can then connect to a running [Keeper](https://github.com/oceanprotocol/keeper-contracts) & [Aquarius](https://github.com/oceanprotocol/aquarius) instance, e.g.:
```js
const ocean = await new Ocean({
nodeUri: 'http://localhost:8545',
aquariusUri: 'http://localhost:5000'
const ocean: Ocean = await Ocean.getInstance({
// the node of the blockchain to connect to, could also be infura
nodeUri: "http://localhost:8545",
// the uri of aquarius
aquariusUri: "http://localhost:5000",
// the uri of brizo
brizoUri: "http://localhost:8030",
// the uri to the parity node you want to use for encryption and decryption
parityUri: "http://localhost:9545",
// the uri of the secret store that holds the keys
secretStoreUri: "http://localhost:12001",
// the threshold of nodes from the secre store that have to agree to the decrypt
threshold: 0,
// the password for the account (in the local parity node) used to sign messages for secret store
password: "unittest",
// the address of the account (in the local parity node) used to sign messages for secret store
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
})
```
### Examples
* [Examples](/src/examples/squid)
* [Tuna](https://github.com/oceanprotocol/tuna/examples/squid)
## Development
To start development you need to:
@ -64,6 +84,29 @@ npm i
npm start
```
### Test
To start unit tests you need to:
```bash
ganache-cli &
npm run test
```
or to watch for changes
```bash
ganache-cli &
npm run test:watch
```
to create code coverage
```bash
ganache-cli &
npm run test:cover
```
This will start a watcher for changes of the code.
### Production build
@ -77,34 +120,10 @@ npm run build
For a new **patch release**, execute on the machine where you're logged into your npm account:
```bash
npm run release
./bumpversion path
```
In case you have 2FA setup on npm.js, pass a code as One Time Password:
```bash
npm run release --otp <yourcode>
```
Command is powered by [`release-it`](https://github.com/webpro/release-it) package, defined in the `package.json`.
That's what the command does without any user interaction:
- create release commit by updating version in `package.json`
- create tag for that release commit
- push commit & tag
- create a new release on GitHub, with change log auto-generated from commit messages
- publish to npm as a new release
If you want to create a **minor** or **major release**, use these commands:
```bash
npm run release-minor
```
```bash
npm run release-major
```
git tag with the latest version and `git push`
## License

2746
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.1.7",
"version": "0.1.21",
"description": "JavaScript client library for Ocean Protocol",
"main": "dist/squid.js",
"scripts": {
@ -47,18 +47,15 @@
"url": "https://github.com/oceanprotocol/squid-js/issues"
},
"homepage": "https://github.com/oceanprotocol/squid-js#readme",
"engines": {
"node": ">=8 <10"
},
"dependencies": {
"@oceanprotocol/keeper-contracts": "0.3.19",
"@oceanprotocol/secret-store-client": "0.0.11",
"@oceanprotocol/keeper-contracts": "0.3.22",
"@oceanprotocol/secret-store-client": "0.0.12",
"bignumber.js": "^8.0.1",
"eth-crypto": "^1.2.5",
"eth-crypto": "^1.2.7",
"eth-ecies": "^1.0.3",
"ethereumjs-util": "^6.0.0",
"jsonwebtoken": "^8.3.0",
"node-fetch": "^2.2.1",
"jsonwebtoken": "^8.4.0",
"node-fetch": "^2.3.0",
"uuid": "^3.3.2",
"web3": "1.0.0-beta.36",
"web3-utils": "1.0.0-beta.36",
@ -67,7 +64,7 @@
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.5",
"@types/node": "^10.12.6",
"@types/node": "^10.12.7",
"chai": "^4.2.0",
"mocha": "^5.2.0",
"nyc": "^13.1.0",

View File

@ -2,9 +2,11 @@ import {URL} from "whatwg-url"
import DDO from "../ddo/DDO"
import Config from "../models/Config"
import Logger from "../utils/Logger"
import AquariusConnectorProvider from "./AquariusConnectorProvider"
import WebServiceConnectorProvider from "../utils/WebServiceConnectorProvider"
import SearchQuery from "./query/SearchQuery"
const apiPath = "/api/v1/aquarius/assets/ddo"
export default class Aquarius {
private url: string
@ -16,7 +18,7 @@ export default class Aquarius {
public async getAccessUrl(accessToken: any, payload: any): Promise<string> {
const accessUrl: string = await AquariusConnectorProvider.getConnector()
const accessUrl: string = await WebServiceConnectorProvider.getConnector()
.post(`${accessToken.service_endpoint}/${accessToken.resource_id}`, payload)
.then((response: any): string => {
if (response.ok) {
@ -37,78 +39,98 @@ export default class Aquarius {
return accessUrl
}
public async queryMetadata(query: SearchQuery): Promise<any[]> {
public async queryMetadata(query: SearchQuery): Promise<DDO[]> {
const result = await AquariusConnectorProvider.getConnector()
.post(this.url + "/api/v1/aquarius/assets/ddo/query", JSON.stringify(query))
const result: DDO[] = await WebServiceConnectorProvider.getConnector()
.post(`${this.url}${apiPath}/query`, JSON.stringify(query))
.then((response: any) => {
if (response.ok) {
return response.json()
return response.json() as DDO[]
}
Logger.error("queryMetadata failed:", response.status, response.statusText)
return null
return [] as DDO[]
})
.then((ddos) => {
return ddos.map((ddo): DDO => {
return new DDO(ddo as DDO)
})
})
.catch((error) => {
Logger.error("Error fetching querying metadata: ", error)
return [] as DDO[]
})
return result
}
public async queryMetadataByText(query: SearchQuery): Promise<any[]> {
public async queryMetadataByText(query: SearchQuery): Promise<DDO[]> {
const fullUrl = new URL(this.url + "/api/v1/aquarius/assets/ddo/query")
const fullUrl = new URL(`${this.url}${apiPath}/query`)
fullUrl.searchParams.append("text", query.text)
fullUrl.searchParams.append("sort", JSON.stringify(query.sort))
fullUrl.searchParams.append("sort", decodeURIComponent(JSON.stringify(query.sort)))
fullUrl.searchParams.append("offset", query.offset.toString())
fullUrl.searchParams.append("page", query.page.toString())
const result = await AquariusConnectorProvider.getConnector()
const result: DDO[] = await WebServiceConnectorProvider.getConnector()
.get(fullUrl)
.then((response: any) => {
if (response.ok) {
return response.json()
return response.json() as DDO[]
}
Logger.log("queryMetadataByText failed:", response.status, response.statusText)
return null
return [] as DDO[]
})
.then((ddos) => {
return ddos.map((ddo): DDO => {
return new DDO(ddo as DDO)
})
})
.catch((error) => {
Logger.error("Error fetching querying metadata: ", error)
Logger.error("Error fetching querying metadata by text: ", error)
return [] as DDO[]
})
return result
}
public async storeDDO(ddo: DDO): Promise<DDO> {
const fullUrl = this.url + `/api/v1/aquarius/assets/ddo`
const result: DDO = await AquariusConnectorProvider.getConnector()
const fullUrl = `${this.url}${apiPath}`
const result: DDO = await WebServiceConnectorProvider.getConnector()
.post(fullUrl, DDO.serialize(ddo))
.then((response: any) => {
if (response.ok) {
return response.json()
}
Logger.error("storeDDO failed:", response.status, response.statusText)
return null
Logger.error("storeDDO failed:", response.status, response.statusText, ddo)
return null as DDO
})
.then((response: DDO) => {
return new DDO(response) as DDO
})
.catch((error) => {
Logger.error("Error fetching querying metadata: ", error)
return null as DDO
})
return result
}
public async retrieveDDO(did: string): Promise<DDO> {
const fullUrl = this.url + `/api/v1/aquarius/assets/ddo/${did}`
const result = await AquariusConnectorProvider.getConnector()
const fullUrl = `${this.url}${apiPath}/${did}`
const result = await WebServiceConnectorProvider.getConnector()
.get(fullUrl)
.then((response: any) => {
if (response.ok) {
return response.json()
}
Logger.log("retrieveDDO failed:", response.status, response.statusText)
return null
Logger.log("retrieveDDO failed:", response.status, response.statusText, did)
return null as DDO
})
.then((response: DDO) => {
return new DDO(response) as DDO
})
.catch((error) => {
Logger.error("Error fetching querying metadata: ", error)
return null as DDO
})
return result

View File

@ -1,19 +0,0 @@
import AquariusConnector from "./AquariusConnector"
export default class AquariusConnectorProvider {
public static setConnector(connector: AquariusConnector) {
AquariusConnectorProvider.connector = connector
}
public static getConnector(): AquariusConnector {
if (!AquariusConnectorProvider.connector) {
AquariusConnectorProvider.connector = new AquariusConnector()
}
return AquariusConnectorProvider.connector
}
private static connector: AquariusConnector = null
}

43
src/brizo/Brizo.ts Normal file
View File

@ -0,0 +1,43 @@
import Config from "../models/Config"
import WebServiceConnectorProvider from "../utils/WebServiceConnectorProvider"
export default class Brizo {
private url: string
constructor(config: Config) {
this.url = config.brizoUri
}
public getPurchaseEndpoint() {
return `${this.url}/api/v1/brizo/services/access/purchase?`
}
public getConsumeEndpoint(pubKey: string, serviceId: string, url: string) {
return `${this.url}/api/v1/brizo/services/consume?pubKey=${pubKey}&serviceId=${serviceId}&url=${url}`
}
public getComputeEndpoint(pubKey: string, serviceId: string, algo: string, container: string) {
// tslint:disable-next-line
return `${this.url}/api/v1/brizo/services/compute?pubKey=${pubKey}&serviceId=${serviceId}&algo=${algo}&container=${container}"`
}
public async initializeServiceAgreement(
did: string,
serviceAgreementId: string,
serviceDefinitionId: string,
signature: string,
consumerPublicKey: string): Promise<any> {
return WebServiceConnectorProvider.getConnector().post(
`${this.url}/api/v1/brizo/services/access/initialize`,
{
did,
serviceAgreementId,
serviceDefinitionId,
signature,
consumerPublicKey,
})
}
}

View File

@ -0,0 +1,20 @@
import ConfigProvider from "../ConfigProvider"
import Brizo from "./Brizo"
export default class BrizoProvider {
public static setBrizo(brizo: Brizo) {
BrizoProvider.brizo = brizo
}
public static getBrizo() {
if (!BrizoProvider.brizo) {
BrizoProvider.brizo = new Brizo(ConfigProvider.getConfig())
}
return BrizoProvider.brizo
}
private static brizo: Brizo = null
}

View File

@ -12,4 +12,5 @@ export default class AdditionalInformation {
mediaType: "text/turtle",
}as StructuredMarkup,
]
public checksum: string
}

View File

@ -1,8 +1,14 @@
import Event from "./Event"
import Parameter from "./Parameter"
export default class Condition {
public name: string = "lockPayment"
public contractName: string = "AccessCondition"
public methodName: string = "lockPayment"
public timeout: number = 0
public conditionKey: string
public conditionKey: string = "0x12122434"
public parameters: Parameter[]
public events: Event[]
public dependencies: string[] = []
public dependencyTimeoutFlags: number[] = []
public isTerminalCondition: boolean = false
}

View File

@ -20,18 +20,37 @@ export default class DDO {
public authentication: Authentication[]
public service: Service[]
// @ts-ignore
private assa: string
public constructor(ddo?: {
id?: string,
publicKey?: PublicKey[],
publicKey?: any[],
authentication?: Authentication[],
service?: Service[],
}) {
this.authentication = ddo ? ddo.authentication ? ddo.authentication : [] : []
this.id = ddo ? ddo.id ? ddo.id : null : null
this.publicKey = ddo ? ddo.publicKey ? ddo.publicKey : [] : []
this.authentication = ddo ? ddo.authentication ? ddo.authentication : [] : []
this.service = ddo ? ddo.service ? ddo.service : [] : []
}
public findServiceById(serviceDefinitionId: string): Service {
if (!serviceDefinitionId) {
throw new Error("serviceDefinitionId not set")
}
const service: Service = this.service.find((s) => s.serviceDefinitionId === serviceDefinitionId)
return service
}
public findServiceByType(serviceType: string): Service {
if (!serviceType) {
throw new Error("serviceType not set")
}
const service: Service = this.service.find((s) => s.type === serviceType)
return service
}
}

7
src/ddo/Event.ts Normal file
View File

@ -0,0 +1,7 @@
import EventHandlers from "./EventHandlers"
export default class Event {
public name: string
public actorType: string[]
public handlers: EventHandlers
}

5
src/ddo/EventHandlers.ts Normal file
View File

@ -0,0 +1,5 @@
export default class EventHandlers {
public moduleName: string = "serviceAgreement"
public functionName: string = "fulfillAgreement"
public version: string = "0.1"
}

View File

@ -3,9 +3,7 @@ import Curation from "./Curation"
import MetaDataBase from "./MetaDataBase"
import StructuredMarkup from "./StructuredMarkup"
export default class MetaData {
public base: MetaDataBase = {
const base: MetaDataBase = {
name: "UK Weather information 2011",
type: "dataset",
description: "Weather information of UK including temperature and humidity",
@ -21,6 +19,7 @@ export default class MetaData {
workExample: "stationId,latitude,longitude,datetime,temperature,humidity423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
contentUrls: [
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
],
links: [
{sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/"},
@ -32,13 +31,13 @@ export default class MetaData {
price: 10,
} as MetaDataBase
public curation: Curation = {
const curation: Curation = {
rating: 0.93,
numVotes: 123,
schema: "Binary Votting",
} as Curation
public additionalInformation: AdditionalInformation = {
const additionalInformation: AdditionalInformation = {
updateFrecuency: "yearly",
structuredMarkup: [
{
@ -51,4 +50,19 @@ export default class MetaData {
} as StructuredMarkup,
],
} as AdditionalInformation
export default class MetaData {
public additionalInformation: AdditionalInformation
public base: MetaDataBase
public curation: Curation
constructor(metaData?: MetaData) {
this.additionalInformation = metaData ?
metaData.additionalInformation ? metaData.additionalInformation :
additionalInformation : additionalInformation
this.base = metaData ? metaData.base ? metaData.base : base : base
this.curation = metaData ? metaData.curation ? metaData.curation : curation : curation
}
}

View File

@ -11,7 +11,8 @@ export default class MetaDataBase {
public compression: string = "zip"
public contentType: string = "text/csv"
public workExample: string = "423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68"
public contentUrls: string[] = [
public contentUrls: string | string[] = [
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
]
public links: any[] = [

View File

@ -0,0 +1,73 @@
import DDO from "../ddo/DDO"
import MetaData from "../ddo/MetaData"
import MetaDataBase from "../ddo/MetaDataBase"
import Service from "../ddo/Service"
import {Account, Logger, Ocean, ServiceAgreement} from "../squid"
(async () => {
const ocean: Ocean = await Ocean.getInstance({
nodeUri: "http://localhost:8545",
aquariusUri: "http://localhost:5000",
brizoUri: "http://localhost:8030",
parityUri: "http://localhost:9545",
secretStoreUri: "http://localhost:12001",
threshold: 0,
password: "unittest",
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
})
const publisher: Account = (await ocean.getAccounts())[0]
const consumer: Account = (await ocean.getAccounts())[1]
const metaData = new MetaData({
base: {
name: "Office Humidity",
type: "dataset",
description: "Weather information of UK including temperature and humidity",
size: "3.1gb",
dateCreated: "2012-02-01T10:55:11+00:00",
author: "Met Office",
license: "CC-BY",
copyrightHolder: "Met Office",
encoding: "UTF-8",
compression: "zip",
contentType: "text/csv",
// tslint:disable-next-line
workExample: "stationId,latitude,longitude,datetime,temperature,humidity423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
contentUrls: [
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
],
links: [
{sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/"},
{sample2: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/"},
{fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/"},
],
inLanguage: "en",
tags: "weather, uk, 2011, temperature, humidity",
price: 10,
} as MetaDataBase,
} as MetaData)
const ddo: DDO = await ocean.registerAsset(metaData, publisher)
Logger.log("did", ddo.id)
const accessService = ddo.findServiceByType("Access")
const serviceAgreementSignatureResult: any = await ocean.signServiceAgreement(ddo.id,
accessService.serviceDefinitionId, consumer)
Logger.log("ServiceAgreement Id:", serviceAgreementSignatureResult.serviceAgreementId)
Logger.log("ServiceAgreement Signature:", serviceAgreementSignatureResult.serviceAgreementSignature)
const service: Service = ddo.findServiceByType("Access")
const serviceAgreement: ServiceAgreement = await ocean.executeServiceAgreement(
ddo.id,
service.serviceDefinitionId,
serviceAgreementSignatureResult.serviceAgreementId,
serviceAgreementSignatureResult.serviceAgreementSignature,
consumer,
publisher)
Logger.log("ServiceAgreement Id:", serviceAgreement.getId())
})()

View File

@ -0,0 +1,75 @@
import DDO from "../ddo/DDO"
import MetaData from "../ddo/MetaData"
import MetaDataBase from "../ddo/MetaDataBase"
import Service from "../ddo/Service"
import {Account, Logger, Ocean} from "../squid"
(async () => {
const ocean: Ocean = await Ocean.getInstance({
nodeUri: "http://localhost:8545",
aquariusUri: "http://localhost:5000",
brizoUri: "http://localhost:8030",
parityUri: "http://localhost:9545",
secretStoreUri: "http://localhost:12001",
threshold: 0,
password: "unittest",
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
})
const publisher: Account = (await ocean.getAccounts())[0]
const consumer: Account = (await ocean.getAccounts())[1]
const metaData = new MetaData({
base: {
name: "Office Humidity",
type: "dataset",
description: "Weather information of UK including temperature and humidity",
size: "3.1gb",
dateCreated: "2012-02-01T10:55:11+00:00",
author: "Met Office",
license: "CC-BY",
copyrightHolder: "Met Office",
encoding: "UTF-8",
compression: "zip",
contentType: "text/csv",
// tslint:disable-next-line
workExample: "stationId,latitude,longitude,datetime,temperature,humidity423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
contentUrls: [
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
],
links: [
{sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/"},
{sample2: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/"},
{fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/"},
],
inLanguage: "en",
tags: "weather, uk, 2011, temperature, humidity",
price: 10,
} as MetaDataBase,
} as MetaData)
const ddo: DDO = await ocean.registerAsset(metaData, publisher)
Logger.log("did", ddo.id)
const accessService = ddo.findServiceByType("Access")
const serviceAgreementSignatureResult: any = await ocean
.signServiceAgreement(
ddo.id,
accessService.serviceDefinitionId,
consumer)
Logger.log("ServiceAgreement Id:", serviceAgreementSignatureResult.serviceAgreementId)
Logger.log("ServiceAgreement Signature:", serviceAgreementSignatureResult.serviceAgreementSignature)
const service: Service = ddo.findServiceByType("Access")
await ocean
.initializeServiceAgreement(
ddo.id,
service.serviceDefinitionId,
serviceAgreementSignatureResult.serviceAgreementId,
serviceAgreementSignatureResult.serviceAgreementSignature,
consumer)
})()

View File

@ -1,20 +0,0 @@
import DDO from "../ddo/DDO"
import MetaData from "../ddo/MetaData"
import {Account, Logger, Ocean} from "../squid"
(async () => {
const ocean: Ocean = await Ocean.getInstance({
nodeUri: "http://localhost:8545",
aquariusUri: "http://localhost:5000",
parityUri: "http://localhost:9545",
secretStoreUri: "https://secret-store.dev-ocean.com",
threshold: 2,
password: "unittest",
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
})
const publisher: Account = (await ocean.getAccounts())[0]
const ddo: DDO = await ocean.registerAsset(new MetaData(), publisher)
Logger.log(ddo.id)
})()

View File

@ -0,0 +1,52 @@
import DDO from "../ddo/DDO"
import MetaData from "../ddo/MetaData"
import MetaDataBase from "../ddo/MetaDataBase"
import {Account, Logger, Ocean} from "../squid"
(async () => {
const ocean: Ocean = await Ocean.getInstance({
nodeUri: "http://localhost:8545",
aquariusUri: "http://localhost:5000",
brizoUri: "http://localhost:8030",
parityUri: "http://localhost:9545",
secretStoreUri: "http://localhost:12001",
threshold: 0,
password: "unittest",
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
})
const publisher: Account = (await ocean.getAccounts())[0]
const metaData = new MetaData({
base: {
name: "Office Humidity",
type: "dataset",
description: "Weather information of UK including temperature and humidity",
size: "3.1gb",
dateCreated: "2012-02-01T10:55:11+00:00",
author: "Met Office",
license: "CC-BY",
copyrightHolder: "Met Office",
encoding: "UTF-8",
compression: "zip",
contentType: "text/csv",
// tslint:disable-next-line
workExample: "stationId,latitude,longitude,datetime,temperature,humidity423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
contentUrls: [
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
],
links: [
{sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/"},
{sample2: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/"},
{fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/"},
],
inLanguage: "en",
tags: "weather, uk, 2011, temperature, humidity",
price: 10,
} as MetaDataBase,
} as MetaData)
const ddo: DDO = await ocean.registerAsset(metaData, publisher)
Logger.log(ddo.id)
})()

View File

@ -0,0 +1,22 @@
import {Account, Logger, Ocean, ServiceAgreementTemplate, Templates} from "../squid"
(async () => {
const ocean: Ocean = await Ocean.getInstance({
nodeUri: "http://localhost:8545",
aquariusUri: "http://localhost:5000",
brizoUri: "http://localhost:8030",
parityUri: "http://localhost:9545",
secretStoreUri: "http://localhost:12001",
threshold: 0,
password: "unittest",
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
})
const templateOwner: Account = (await ocean.getAccounts())[5]
const serviceAgreementTemplate: ServiceAgreementTemplate = new ServiceAgreementTemplate(new Templates.Access())
const serviceAgreementRegistered: boolean = await serviceAgreementTemplate.register(templateOwner.getId())
Logger.log("ServiceAgreement registered:", serviceAgreementRegistered,
"templateId:", serviceAgreementTemplate.getId())
})()

25
src/examples/Search.ts Normal file
View File

@ -0,0 +1,25 @@
import DDO from "../ddo/DDO"
import {Logger, Ocean} from "../squid"
(async () => {
const ocean: Ocean = await Ocean.getInstance({
nodeUri: "http://localhost:8545",
aquariusUri: "http://localhost:5000",
brizoUri: "http://localhost:8030",
parityUri: "http://localhost:9545",
secretStoreUri: "http://localhost:12001",
threshold: 0,
password: "unittest",
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
})
const result: DDO[] = await ocean.searchAssetsByText("Office Humidity")
const names: string[] = result.map((ddo: DDO): string => {
const service = ddo.findServiceByType("Metadata")
if (service && service.metadata) {
return service.metadata.base.name
}
})
Logger.log(names.length, names)
})()

View File

@ -0,0 +1,85 @@
import AdditionalInformation from "../ddo/AdditionalInformation"
import Curation from "../ddo/Curation"
import DDO from "../ddo/DDO"
import MetaData from "../ddo/MetaData"
import MetaDataBase from "../ddo/MetaDataBase"
import Service from "../ddo/Service"
import {Account, Logger, Ocean} from "../squid"
(async () => {
const ocean: Ocean = await Ocean.getInstance({
nodeUri: "http://localhost:8545",
aquariusUri: "http://localhost:5000",
brizoUri: "http://localhost:8030",
parityUri: "http://localhost:9545",
secretStoreUri: "http://localhost:12001",
threshold: 0,
password: "unittest",
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
})
const publisher: Account = (await ocean.getAccounts())[0]
const consumer: Account = (await ocean.getAccounts())[1]
const metaData = {
additionalInformation: {
structuredMarkup: [
{
mediaType: "application/ld+json",
uri: "http://skos.um.es/unescothes/C01194/jsonld",
},
{
mediaType: "text/turtle",
uri: "http://skos.um.es/unescothes/C01194/turtle",
},
],
updateFrecuency: "yearly",
checksum: "efdd14d39feb726e321931f408b3454d26f1a4899bcc608a68b5397f23203174",
} as AdditionalInformation,
base: {
name: "Office Humidity",
type: "dataset",
description: "Weather information of UK including temperature and humidity",
size: "3.1gb",
dateCreated: "2012-02-01T10:55:11+00:00",
author: "Met Office",
license: "CC-BY",
copyrightHolder: "Met Office",
encoding: "UTF-8",
compression: "zip",
contentType: "text/csv",
// tslint:disable-next-line
workExample: "stationId,latitude,longitude,datetime,temperature,humidity423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
contentUrls: [
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
],
links: [
{sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/"},
{sample2: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/"},
{fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/"},
],
inLanguage: "en",
tags: "weather, uk, 2011, temperature, humidity",
price: 10,
} as MetaDataBase,
curation: {
rating: 0.94,
numVotes: 124,
schema: "Binary Votting",
} as Curation,
}
const ddo: DDO = await ocean.registerAsset(metaData as MetaData, publisher)
Logger.log("did", ddo.id)
const accessService: Service = ddo.findServiceByType("Access")
const serviceAgreementResult: any = await ocean
.signServiceAgreement(
ddo.id,
accessService.serviceDefinitionId,
consumer)
Logger.log("ServiceAgreement Id:", serviceAgreementResult.serviceAgreementId)
Logger.log("ServiceAgreement Signature:", serviceAgreementResult.serviceAgreementSignature)
})()

9
src/examples/fire_lazers.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
set -e
npm run run src/examples/RegisterServiceAgreementTemplates.ts
npm run run src/examples/RegisterAsset.ts
npm run run src/examples/Search.ts
npm run run src/examples/SignAgreement.ts
npm run run src/examples/ExecuteAgreement.ts

View File

@ -15,11 +15,11 @@ export default class ContractHandler {
}
}
public static set(name: string, contractInstance: Contract) {
protected static set(name: string, contractInstance: Contract) {
ContractHandler.contracts.set(name, contractInstance)
}
public static has(name: string): boolean {
protected static has(name: string): boolean {
return ContractHandler.contracts.has(name)
}

View File

@ -3,16 +3,14 @@ import GenericContract from "./contracts/GenericContract"
export default class ContractReflector {
public static async reflectContractMethod(pathToMethod: string): Promise<MethodReflection> {
const parts: string[] = pathToMethod.split(".")
const contract = await GenericContract.getInstance(parts[0])
public static async reflectContractMethod(contractName: string, methodName: string): Promise<MethodReflection> {
const contract = await GenericContract.getInstance(contractName)
return {
contractName: parts[0],
methodName: parts[1],
contractName,
methodName,
address: contract.getAddress(),
signature: contract.getSignatureOfMethod(parts[1]),
inputs: contract.getInputsOfMethod(parts[1]),
signature: contract.getSignatureOfMethod(methodName),
inputs: contract.getInputsOfMethod(methodName),
} as MethodReflection
}
}

View File

@ -1,5 +1,6 @@
import OceanAuth from "./contracts/Auth"
import AccessConditions from "./contracts/conditions/AccessConditions"
import PaymentConditions from "./contracts/conditions/PaymentConditions"
import DIDRegistry from "./contracts/DIDRegistry"
import OceanMarket from "./contracts/Market"
import ServiceAgreement from "./contracts/ServiceAgreement"
@ -18,6 +19,7 @@ export default class Keeper {
Keeper.instance.token = await OceanToken.getInstance()
Keeper.instance.serviceAgreement = await ServiceAgreement.getInstance()
Keeper.instance.accessConditions = await AccessConditions.getInstance()
Keeper.instance.paymentConditions = await PaymentConditions.getInstance()
Keeper.instance.didRegistry = await DIDRegistry.getInstance()
}
return Keeper.instance
@ -30,12 +32,13 @@ export default class Keeper {
public auth: OceanAuth
public serviceAgreement: ServiceAgreement
public accessConditions: AccessConditions
public paymentConditions: PaymentConditions
public didRegistry: DIDRegistry
public async getNetworkName(): Promise<string> {
return Web3Provider.getWeb3().eth.net.getId()
.then((networkId) => {
let network: string = "unknown"
let network: string = "Unknown"
switch (networkId) {
case 1:
@ -53,8 +56,11 @@ export default class Keeper {
case 42:
network = "Kovan"
break
case 8995:
network = "Ocean_POA_AWS"
break
default:
network = "development"
network = "Development"
}
return network
})

View File

@ -60,19 +60,25 @@ export default abstract class ContractBase {
if (!this.contract.methods[name]) {
throw new Error(`Method "${name}" is not part of contract "${this.contractName}"`)
}
const method = this.contract.methods[name]
try {
const tx = this.contract.methods[name](...args)
const gas = await tx.estimateGas(args, {
const tx = method(...args)
const estimatedGas = await tx.estimateGas(args, {
from,
})
return tx.send({
from,
gas,
gas: estimatedGas,
})
} catch (err) {
const argString = JSON.stringify(args, null, 2)
const mappedArgs = this.searchMethod(name).inputs.map((input, i) => {
return {
name: input.name,
value: args[i],
}
})
Logger.error(`Sending transaction "${name}" on contract "${this.contractName}" failed.`)
Logger.error(`Args: ${argString} From: ${from}`)
Logger.error(`Parameters: ${JSON.stringify(mappedArgs, null, 2)} from: ${from}`)
throw err
}
}

View File

@ -11,12 +11,14 @@ export default class ServiceAgreement extends ContractBase {
}
public async setupAgreementTemplate(templateId: string, methodReflections: MethodReflection[],
dependencyMatrix: number[], name: any, ownerAddress: string)
dependencyMatrix: number[], name: any, fulfillmentIndices: number[],
fulfillmentOperator: number,
ownerAddress: string)
: Promise<Receipt> {
return this.send("setupAgreementTemplate", ownerAddress, [
templateId, methodReflections.map((r) => r.address),
methodReflections.map((r) => r.signature), dependencyMatrix, name, [0], 0,
methodReflections.map((r) => r.signature), dependencyMatrix, name, fulfillmentIndices, fulfillmentOperator,
])
}

View File

@ -1,3 +1,4 @@
import {Receipt} from "web3-utils"
import ContractBase from "../ContractBase"
export default class PaymentConditions extends ContractBase {
@ -7,4 +8,11 @@ export default class PaymentConditions extends ContractBase {
await paymentConditions.init()
return paymentConditions
}
public async lockPayment(serviceAgreementId: string, assetId: string, price: number, consumerAddress: string)
: Promise<Receipt> {
return this.send("lockPayment", consumerAddress, [
serviceAgreementId, "0x" + assetId, price,
])
}
}

View File

@ -3,6 +3,10 @@ export default class Config {
// the url to the aquarius
public aquariusUri: string
/* Brizo Config */
// the url to the brizo
public brizoUri: string
/* Keeper Config */
// the uri to the node we want to connect to, not need if web3Provider is set
public nodeUri?: string

4
src/models/InputType.ts Normal file
View File

@ -0,0 +1,4 @@
export default class InputType {
public name: string
public type: string
}

View File

@ -1,7 +1,9 @@
import InputType from "./InputType"
export default class MethodReflection {
public contractName: string
public methodName: string
public address: string
public signature: string
public inputs: any[]
public inputs: InputType[]
}

View File

@ -1,4 +1,5 @@
import BigNumber from "bignumber.js"
import * as EthJsUtils from "ethereumjs-util"
import Keeper from "../keeper/Keeper"
import Web3Provider from "../keeper/Web3Provider"
import Balance from "../models/Balance"
@ -33,9 +34,19 @@ export default class Account extends OceanBase {
return this.balance
}
// Transactions with gas cost
public async requestTokens(amount: number): Promise<number> {
await (await Keeper.getInstance()).market.requestTokens(amount, this.id)
return amount
}
public async getPublicKey(): Promise<string> {
const web3 = Web3Provider.getWeb3()
const msg = web3.utils.sha3(this.getId())
const sig = await web3.eth.sign(msg, this.getId())
const {v, r, s} = EthJsUtils.fromRpcSig(sig)
return EthJsUtils.ecrecover(EthJsUtils.toBuffer(msg), v, r, s).toString("hex")
}
}

View File

@ -1,17 +1,21 @@
import Aquarius from "../aquarius/Aquarius"
import AquariusProvider from "../aquarius/AquariusProvider"
import SearchQuery from "../aquarius/query/SearchQuery"
import BrizoProvider from "../brizo/BrizoProvider"
import ConfigProvider from "../ConfigProvider"
import Authentication from "../ddo/Authentication"
import DDOCondition from "../ddo/Condition"
import DDO from "../ddo/DDO"
import Event from "../ddo/Event"
import EventHandlers from "../ddo/EventHandlers"
import MetaData from "../ddo/MetaData"
import Parameter from "../ddo/Parameter"
import Service from "../ddo/Service"
import Keeper from "../keeper/Keeper"
import Web3Provider from "../keeper/Web3Provider"
import Config from "../models/Config"
import ValuePair from "../models/ValuePair"
import InputType from "../models/InputType"
import ValueType from "../models/ValueType"
import SecretStoreProvider from "../secretstore/SecretStoreProvider"
import Logger from "../utils/Logger"
import Account from "./Account"
import IdGenerator from "./IdGenerator"
@ -28,7 +32,6 @@ export default class Ocean {
ConfigProvider.setConfig(config)
Ocean.instance = new Ocean()
Ocean.instance.keeper = await Keeper.getInstance()
Ocean.instance.aquarius = await AquariusProvider.getAquarius()
}
return Ocean.instance
@ -37,7 +40,6 @@ export default class Ocean {
private static instance = null
private keeper: Keeper
private aquarius: Aquarius
private constructor() {
}
@ -50,20 +52,23 @@ export default class Ocean {
return ethAccounts.map((address: string) => new Account(address))
}
public async resolveDID(did): Promise<DDO> {
return AquariusProvider.getAquarius().retrieveDDO(did)
}
public async registerAsset(metadata: MetaData, publisher: Account): Promise<DDO> {
const {didRegistry} = this.keeper
const aquarius = AquariusProvider.getAquarius()
const brizo = BrizoProvider.getBrizo()
const id: string = IdGenerator.generateId()
const did: string = `did:op:${id}`
const serviceDefinitionId: string = IdGenerator.generatePrefixedId()
metadata.base.contentUrls = metadata.base.contentUrls.map((contentUrl) => {
// todo encrypt url in secret store
Logger.log(contentUrl)
return "0x00000"
})
metadata.base.contentUrls =
await SecretStoreProvider.getSecretStore().encryptDocument(id, metadata.base.contentUrls)
const template = new Access()
const serviceAgreementTemplate = new ServiceAgreementTemplate(template)
@ -72,31 +77,90 @@ export default class Ocean {
const conditions: Condition[] = await serviceAgreementTemplate.getConditions()
// create ddo conditions out of the keys
const ddoConditions: DDOCondition[] = conditions.map((condition: Condition): DDOCondition => {
return {
name: condition.methodReflection.methodName,
timeout: condition.timeout,
conditionKey: condition.condtionKey,
parameters: condition.methodReflection.inputs.map((input: ValuePair) => {
return {
...input,
value: "xxx",
} as Parameter
}),
const ddoConditions: DDOCondition[] = conditions
.map((condition: Condition, index: number): DDOCondition => {
const events: Event[] = [
{
name: "PaymentReleased",
actorType: [
"consumer",
],
handlers: {
moduleName: "serviceAgreement",
functionName: "fulfillAgreement",
version: "0.1",
} as EventHandlers,
} as Event,
]
const mapParameterValueToName = (name) => {
switch (name) {
case "price":
return metadata.base.price
case "assetId":
return "0x" + id
case "documentKeyId":
return "0x1234"
}
return null
}
const parameters: Parameter[] = condition.methodReflection.inputs.map((input: InputType) => {
return {
name: input.name,
type: input.type,
value: mapParameterValueToName(input.name),
} as Parameter
})
// Logger.log(`${condition.methodReflection.contractName}.${condition.methodReflection.methodName}`,
// JSON.stringify(parameters, null, 2))
return {
contractName: condition.methodReflection.contractName,
methodName: condition.methodReflection.methodName,
timeout: condition.timeout,
index,
conditionKey: condition.condtionKey,
parameters,
events,
dependencies: condition.dependencies,
dependencyTimeoutFlags: condition.dependencyTimeoutFlags,
isTerminalCondition: condition.isTerminalCondition,
} as DDOCondition
})
const serviceEndpoint = this.aquarius.getServiceEndpoint(did)
const serviceEndpoint = aquarius.getServiceEndpoint(did)
// create ddo itself
const ddo: DDO = new DDO({
authentication: [{
type: "RsaSignatureAuthentication2018",
publicKey: did + "#keys-1",
} as Authentication],
id: did,
publicKey: [
{
id: did + "#keys-1",
},
{
type: "Ed25519VerificationKey2018",
},
{
owner: did,
},
{
publicKeyBase58: await publisher.getPublicKey(),
},
],
service: [
{
type: template.templateName,
// tslint:disable-next-line
serviceEndpoint: "http://mybrizo.org/api/v1/brizo/services/consume?pubKey=${pubKey}&serviceId={serviceId}&url={url}",
purchaseEndpoint: "http://mybrizo.org/api/v1/brizo/services/access/purchase?",
purchaseEndpoint: brizo.getPurchaseEndpoint(),
serviceEndpoint: brizo.getConsumeEndpoint(publisher.getId(),
serviceDefinitionId, metadata.base.contentUrls[0]),
// the id of the service agreement?
serviceDefinitionId,
// the id of the service agreement template
@ -104,41 +168,99 @@ export default class Ocean {
conditions: ddoConditions,
} as Service,
{
type: "Compute",
serviceEndpoint: brizo.getComputeEndpoint(publisher.getId(),
serviceDefinitionId, "xxx", "xxx"),
} as Service,
{
type: "Metadata",
serviceEndpoint,
metadata,
} as Service,
],
})
const storedDdo = await this.aquarius.storeDDO(ddo)
const storedDdo = await aquarius.storeDDO(ddo)
await didRegistry.registerAttribute(id, ValueType.DID, "Metadata", serviceEndpoint,
await didRegistry.registerAttribute(
id,
ValueType.DID,
"Metadata",
serviceEndpoint,
publisher.getId())
return storedDdo
}
public async purchase(did: string, consumer: Account): Promise<ServiceAgreement> {
public async signServiceAgreement(did: string,
serviceDefinitionId: string,
consumer: Account): Promise<any> {
const ddo = await AquariusProvider.getAquarius().retrieveDDO(did)
const id = did.replace("did:op:", "")
const serviceAgreementId: string = IdGenerator.generateId()
try {
const serviceAgreementSignature: string = await ServiceAgreement.signServiceAgreement(id,
ddo, serviceDefinitionId, serviceAgreementId, consumer)
return {
serviceAgreementId,
serviceAgreementSignature,
}
} catch (err) {
Logger.error("Signing ServiceAgreement failed!", err)
}
}
public async initializeServiceAgreement(did: string,
serviceDefinitionId: string,
serviceAgreementId: string,
serviceAgreementSignature: string,
consumer: Account) {
const result = await BrizoProvider
.getBrizo()
.initializeServiceAgreement(
did,
serviceAgreementId,
serviceDefinitionId,
serviceAgreementSignature,
await consumer.getPublicKey())
Logger.log(result)
}
public async executeServiceAgreement(did: string,
serviceDefinitionId: string,
serviceAgreementId: string,
serviceAgreementSignature: string,
consumer: Account,
publisher: Account): Promise<ServiceAgreement> {
const ddo = await AquariusProvider.getAquarius().retrieveDDO(did)
const id = did.replace("did:op:", "")
const serviceAgreementId: string = IdGenerator.generateId()
const serviceAgreement: ServiceAgreement = await ServiceAgreement.signServiceAgreement(id,
// todo get publisher from ddo
ddo, serviceAgreementId, consumer, new Account())
const serviceAgreement: ServiceAgreement = await ServiceAgreement
.executeServiceAgreement(
id,
ddo,
serviceDefinitionId,
serviceAgreementId,
serviceAgreementSignature,
consumer,
publisher)
return serviceAgreement
}
public async searchAssets(query: SearchQuery): Promise<any[]> {
return this.aquarius.queryMetadata(query)
public async searchAssets(query: SearchQuery): Promise<DDO[]> {
return AquariusProvider.getAquarius().queryMetadata(query)
}
public async searchAssetsByText(text: string): Promise<any[]> {
return this.aquarius.queryMetadataByText({
public async searchAssetsByText(text: string): Promise<DDO[]> {
return AquariusProvider.getAquarius().queryMetadataByText({
text,
page: 1,
page: 0,
offset: 100,
query: {
value: 1,

View File

@ -3,5 +3,8 @@ import MethodReflection from "../../models/MethodReflection"
export default class Condition {
public methodReflection: MethodReflection
public condtionKey: string
public dependencies: string[]
public dependencyTimeoutFlags: number[]
public isTerminalCondition: boolean
public timeout: number
}

View File

@ -1,5 +1,9 @@
export default class Method {
public path: string
public dependency: number
public name: string
public contractName: string
public methodName: string
public timeout: number
public dependencies: string[]
public dependencyTimeoutFlags: number[]
public isTerminalCondition: boolean
}

View File

@ -1,66 +1,130 @@
import Condition from "../../ddo/Condition"
import DDO from "../../ddo/DDO"
import Service from "../../ddo/Service"
import Keeper from "../../keeper/Keeper"
import Web3Provider from "../../keeper/Web3Provider"
import ValuePair from "../../models/ValuePair"
import Logger from "../../utils/Logger"
import Account from "../Account"
import OceanBase from "../OceanBase"
export default class ServiceAgreement extends OceanBase {
public static async signServiceAgreement(assetId: string, ddo: DDO, serviceAgreementId: string, consumer: Account,
publisher: Account):
Promise<ServiceAgreement> {
public static async signServiceAgreement(assetId: string,
ddo: DDO,
serviceDefinitionId: string,
serviceAgreementId: string,
consumer: Account): Promise<string> {
const values: ValuePair[] = ServiceAgreement.getValuesFromDDO(ddo, serviceAgreementId)
// Logger.log("signing SA", serviceAgreementId)
const service: Service = ddo.findServiceById(serviceDefinitionId)
const values: ValuePair[] = ServiceAgreement.getValuesFromService(service, serviceAgreementId)
const valueHashes = ServiceAgreement.createValueHashes(values)
const timeoutValues: number[] = ServiceAgreement.getTimeoutValuesFromDDO(ddo)
const timeoutValues: number[] = ServiceAgreement.getTimeoutValuesFromService(service)
const serviceAgreementHashSignature = await ServiceAgreement.createSAHashSignature(ddo, serviceAgreementId,
values, valueHashes, timeoutValues, consumer)
const serviceAgreement: ServiceAgreement = await ServiceAgreement.executeAgreement(ddo,
serviceAgreementId, values, valueHashes, timeoutValues, serviceAgreementHashSignature, consumer, publisher)
return serviceAgreement
}
public static async createSAHashSignature(ddo: DDO, serviceAgreementId: string, values: ValuePair[],
valueHashes: string[], timeoutValues: number[], consumer: Account):
Promise<string> {
const conditionKeys: string[] = ddo.service[0].conditions.map((condition) => {
return condition.conditionKey
})
const serviceAgreementHash = ServiceAgreement.hashServiceAgreement(ddo.service[0].templateId,
serviceAgreementId, conditionKeys, valueHashes, timeoutValues)
const serviceAgreementHashSignature =
await Web3Provider.getWeb3().eth.sign(serviceAgreementHash, consumer.getId())
const serviceAgreementHashSignature = await ServiceAgreement
.createSAHashSignature(
service,
serviceAgreementId,
values,
valueHashes,
timeoutValues,
consumer)
return serviceAgreementHashSignature
}
private static async executeAgreement(ddo: DDO, serviceAgreementId: string, values: ValuePair[],
valueHashes: string[], timeoutValues: number[],
serviceAgreementHashSignature: string, consumer: Account,
public static async executeServiceAgreement(assetId: string,
ddo: DDO,
serviceDefinitionId: string,
serviceAgreementId: string,
serviceAgreementHashSignature: string,
consumer: Account,
publisher: Account): Promise<ServiceAgreement> {
// Logger.log("executing SA", serviceAgreementId)
const service: Service = ddo.findServiceById(serviceDefinitionId)
const values: ValuePair[] = ServiceAgreement.getValuesFromService(service, serviceAgreementId)
const valueHashes = ServiceAgreement.createValueHashes(values)
const timeoutValues: number[] = ServiceAgreement.getTimeoutValuesFromService(service)
// todo get consumer from ddo
const serviceAgreement: ServiceAgreement = await ServiceAgreement.executeAgreement(ddo,
serviceDefinitionId, serviceAgreementId, valueHashes, timeoutValues, serviceAgreementHashSignature,
consumer.getId(), publisher)
return serviceAgreement
}
private static async createSAHashSignature(service: Service,
serviceAgreementId: string,
values: ValuePair[],
valueHashes: string[],
timeoutValues: number[],
consumer: Account):
Promise<string> {
if (!service.templateId) {
throw new Error("TemplateId not found in ddo.")
}
const conditionKeys: string[] = service.conditions.map((condition) => {
return condition.conditionKey
})
const serviceAgreementHash = ServiceAgreement
.hashServiceAgreement(
service.templateId,
serviceAgreementId,
conditionKeys,
valueHashes,
timeoutValues)
const serviceAgreementHashSignature = await Web3Provider
.getWeb3().eth.sign(serviceAgreementHash, consumer.getId())
return serviceAgreementHashSignature
}
private static async executeAgreement(ddo: DDO,
serviceDefinitionId: string,
serviceAgreementId: string,
valueHashes: string[],
timeoutValues: number[],
serviceAgreementHashSignature: string,
consumerAddress: string,
publisher: Account): Promise<ServiceAgreement> {
const {serviceAgreement} = await Keeper.getInstance()
const executeAgreementReceipt = await serviceAgreement.executeAgreement(
ddo.service[0].templateId, serviceAgreementHashSignature, consumer.getId(), valueHashes,
timeoutValues, serviceAgreementId, ddo.id, publisher.getId())
const service: Service = ddo.findServiceById(serviceDefinitionId)
if (!service.templateId) {
throw new Error(`TemplateId not found in service "${service.type}" ddo.`)
}
const executeAgreementReceipt = await serviceAgreement
.executeAgreement(
service.templateId,
serviceAgreementHashSignature,
consumerAddress,
valueHashes,
timeoutValues,
serviceAgreementId,
ddo.id,
publisher.getId())
if (executeAgreementReceipt.events.ExecuteAgreement.returnValues.state === false) {
throw new Error("signing service agreement failed.")
throw new Error("executing service agreement failed.")
}
return new ServiceAgreement(
executeAgreementReceipt.events.ExecuteAgreement.returnValues.serviceAgreementId,
ddo,
publisher,
consumer,
new Account(consumerAddress),
executeAgreementReceipt.events.ExecuteAgreement.returnValues.state,
executeAgreementReceipt.events.ExecuteAgreement.returnValues.status,
)
@ -73,12 +137,18 @@ export default class ServiceAgreement extends OceanBase {
}
private static hashSingleValue(data: ValuePair): string {
try {
return Web3Provider.getWeb3().utils.soliditySha3(data).toString("hex")
} catch (err) {
Logger.error(`Hashing of ${JSON.stringify(data, null, 2)} failed.`)
}
}
private static hashServiceAgreement(serviceAgreementTemplateId: string, serviceAgreementId: string,
conditionKeys: string[], valueHashes: string[], timeouts: number[])
: string {
private static hashServiceAgreement(serviceAgreementTemplateId: string,
serviceAgreementId: string,
conditionKeys: string[],
valueHashes: string[],
timeouts: number[]): string {
const args = [
{type: "bytes32", value: serviceAgreementTemplateId} as ValuePair,
{type: "bytes32[]", value: conditionKeys} as ValuePair,
@ -90,31 +160,51 @@ export default class ServiceAgreement extends OceanBase {
return Web3Provider.getWeb3().utils.soliditySha3(...args).toString("hex")
}
private static getTimeoutValuesFromDDO(ddo: DDO): number[] {
const timeoutValues: number[] = ddo.service[0].conditions.map((condition: Condition) => {
private static getTimeoutValuesFromService(service: Service): number[] {
const timeoutValues: number[] = service.conditions.map((condition: Condition) => {
return condition.timeout
})
return timeoutValues
}
private static getValuesFromDDO(ddo: DDO, serviceAgreementId: string): ValuePair[] {
const values: ValuePair[] = [
{type: "bool", value: true} as ValuePair,
{type: "bool", value: false} as ValuePair,
{type: "bool", value: false} as ValuePair,
{type: "uint", value: 120} as ValuePair,
{type: "string", value: serviceAgreementId} as ValuePair,
]
private static getValuesFromService(service: Service, serviceAgreementId: string): ValuePair[] {
const values: ValuePair[] = []
service.conditions.forEach((condition) => {
condition.parameters.forEach((parameter) => {
values.push({
type: parameter.type,
value: parameter.name === "serviceId" ? "0x" + serviceAgreementId : parameter.value,
} as ValuePair)
})
})
// Logger.log("Values", JSON.stringify(values, null, 2))
return values
}
private constructor(serviceAgreementId: string, ddo: DDO, private publisher: Account, consumer: Account,
state: boolean, status: boolean) {
private constructor(serviceAgreementId: string,
ddo: DDO,
private publisher: Account,
consumer: Account,
state: boolean,
status: boolean) {
super(serviceAgreementId)
}
public async lockPayment(assetId: string, price: number, consumer: Account): Promise<boolean> {
const {paymentConditions} = await Keeper.getInstance()
const lockPaymentRceipt =
await paymentConditions.lockPayment(this.getId(), assetId, price,
consumer.getId())
return lockPaymentRceipt.status
}
public async grantAccess(assetId: string, documentId: string): Promise<boolean> {
const {accessConditions} = await Keeper.getInstance()

View File

@ -31,35 +31,54 @@ export default class ServiceAgreementTemplate extends OceanBase {
const dependencyMatrix: number[] =
await Promise.all(this.template.Methods.map(async (method: Method) => {
// tslint:disable-next-line
return method.dependency | method.timeout
return this.compressDependencies(method.dependencies, method.dependencyTimeoutFlags)
}))
const fulfillmentIndices: number[] = this.template.Methods
.map((method: Method, i: number) => method.isTerminalCondition ? i : undefined)
.filter((index: number) => index !== undefined)
const {serviceAgreement} = await Keeper.getInstance()
const methodReflections = await this.getMethodReflections()
const owner = await this.getOwner()
if (!owner.getId().startsWith("0x0")) {
Logger.error(`Template with id "${this.template.id}" already registered.`)
if (owner.getId() === templateOwnerAddress) {
// tslint:disable-next-line
Logger.error(`Template with id "${this.template.id}" is already registered to your account "${templateOwnerAddress}".`)
return false
}
const receipt = await serviceAgreement.setupAgreementTemplate(
this.template.id, methodReflections, dependencyMatrix,
Web3Provider.getWeb3().utils.fromAscii(this.template.templateName),
templateOwnerAddress)
const templateId = receipt.events.SetupAgreementTemplate.returnValues.serviceTemplateId
if (templateId !== this.template.id) {
// tslint:disable-next-line
throw new Error(`TemplateId missmatch on ${this.template.templateName}! Should be "${this.template.id}" but is ${templateId}`)
if (!owner.getId().startsWith("0x0")) {
Logger.error(`Template with id "${this.template.id}" already registered by someone else.`)
return false
}
if (receipt.status) {
Logger.error("Registering template failed")
const receipt = await serviceAgreement
.setupAgreementTemplate(
this.template.id,
methodReflections,
dependencyMatrix,
Web3Provider.getWeb3().utils.fromAscii(this.template.templateName),
fulfillmentIndices,
this.template.fulfillmentOperator,
templateOwnerAddress)
const {serviceTemplateId, provider} = receipt.events.SetupAgreementTemplate.returnValues
if (serviceTemplateId !== this.template.id) {
// tslint:disable-next-line
throw new Error(`TemplateId missmatch on ${this.template.templateName}! Should be "${this.template.id}" but is ${serviceTemplateId}`)
}
if (provider !== templateOwnerAddress) {
// tslint:disable-next-line
throw new Error(`Template owner missmatch on ${this.template.templateName}! Should be "${templateOwnerAddress}" but is ${provider}`)
}
if (!receipt.status) {
Logger.error(`Registering template failed, status was "false".`)
}
return receipt.status
@ -83,23 +102,54 @@ export default class ServiceAgreementTemplate extends OceanBase {
const methodReflections = await this.getMethodReflections()
const conditions: Condition[] = methodReflections.map((methodReflection, i) => {
const method: Method = this.template.Methods[i]
return {
methodReflection,
timeout: this.template.Methods[i].timeout,
condtionKey: ServiceAgreementTemplate.generateConditionsKey(this.getId(),
methodReflection),
timeout: method.timeout,
dependencies: method.dependencies,
dependencyTimeoutFlags: method.dependencyTimeoutFlags,
isTerminalCondition: method.isTerminalCondition,
condtionKey: ServiceAgreementTemplate
.generateConditionsKey(this.getId(), methodReflection),
} as Condition
})
return conditions
}
private compressDependencies(dependencies: string[], dependencyTimeoutFlags: number[]): number {
if (dependencies.length !== dependencyTimeoutFlags.length) {
throw new Error("Deps and timeouts need the same length")
}
// map name to index
const mappedDependencies: number[] = dependencies.map((dep: string) => {
return this.template.Methods.findIndex((m) => m.name === dep)
})
let compressedDependencyValue = 0
const numBits = 2 // 1st for dependency, 2nd for timeout flag
for (let i = 0; i < mappedDependencies.length; i++) {
const dependencyIndex = mappedDependencies[i]
const timeout = dependencyTimeoutFlags[i]
const offset = i * numBits
// tslint:disable-next-line
compressedDependencyValue |= dependencyIndex * 2 ** (offset + 0) // the dependency bit
// tslint:disable-next-line
compressedDependencyValue |= timeout * 2 ** (offset + 1) // the timeout bit
}
return compressedDependencyValue
}
private async getMethodReflections(): Promise<MethodReflection[]> {
const methodReflections: MethodReflection[] =
await Promise.all(this.template.Methods.map(async (method: Method) => {
const methodReflection = await ContractReflector.reflectContractMethod(method.path)
return methodReflection
}))
const methodReflections: MethodReflection[] = []
for (const method of this.template.Methods) {
methodReflections.push(
await ContractReflector.reflectContractMethod(method.contractName, method.methodName),
)
}
return methodReflections
}
}

View File

@ -7,24 +7,40 @@ export default class Access extends TemplateBase {
public id: string = "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"
public Methods: Method[] = [
{
path: "PaymentConditions.lockPayment",
dependency: 0,
name: "lockPayment",
contractName: "PaymentConditions",
methodName: "lockPayment",
timeout: 0,
dependencies: [],
dependencyTimeoutFlags: [],
isTerminalCondition: false,
} as Method,
{
name: "grantAccess",
contractName: "AccessConditions",
methodName: "grantAccess",
timeout: 10,
dependencies: ["lockPayment"],
dependencyTimeoutFlags: [0],
isTerminalCondition: false,
} as Method,
{
path: "AccessConditions.grantAccess",
dependency: 1,
timeout: 500,
name: "releasePayment",
contractName: "PaymentConditions",
methodName: "releasePayment",
timeout: 10,
dependencies: ["grantAccess"],
dependencyTimeoutFlags: [0],
isTerminalCondition: true,
} as Method,
{
path: "PaymentConditions.releasePayment",
dependency: 4,
timeout: 17,
} as Method,
{
path: "PaymentConditions.refundPayment",
dependency: 1,
timeout: 40,
name: "refundPayment",
contractName: "PaymentConditions",
methodName: "refundPayment",
timeout: 10,
dependencies: ["lockPayment", "grantAccess"],
dependencyTimeoutFlags: [0, 1],
isTerminalCondition: true,
} as Method,
]
}

View File

@ -7,24 +7,40 @@ export default class FitchainCompute extends TemplateBase {
public id: string = "0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6"
public Methods: Method[] = [
{
path: "PaymentConditions.lockPayment",
dependency: 0,
name: "lockPayment",
contractName: "PaymentConditions",
methodName: "lockPayment",
timeout: 0,
dependencies: [],
dependencyTimeoutFlags: [],
isTerminalCondition: false,
} as Method,
{
name: "grantAccess",
contractName: "AccessConditions",
methodName: "grantAccess",
timeout: 10,
dependencies: ["lockPayment"],
dependencyTimeoutFlags: [0],
isTerminalCondition: false,
} as Method,
{
path: "AccessConditions.grantAccess",
dependency: 1,
timeout: 500,
name: "releasePayment",
contractName: "PaymentConditions",
methodName: "releasePayment",
timeout: 10,
dependencies: ["grantAccess"],
dependencyTimeoutFlags: [0],
isTerminalCondition: true,
} as Method,
{
path: "PaymentConditions.releasePayment",
dependency: 4,
timeout: 17,
} as Method,
{
path: "PaymentConditions.refundPayment",
dependency: 1,
timeout: 40,
name: "refundPayment",
contractName: "PaymentConditions",
methodName: "refundPayment",
timeout: 10,
dependencies: ["lockPayment", "grantAccess"],
dependencyTimeoutFlags: [0, 1],
isTerminalCondition: true,
} as Method,
]
}

View File

@ -3,5 +3,6 @@ import Method from "../Method"
export default abstract class TemplateBase {
public Methods: Method[]
public templateName: string
public fulfillmentOperator: number = 1
public id: string = "0x00000000000000000000000000000000000000000000000000000000000000"
}

View File

@ -1,11 +1,18 @@
import Account from "./ocean/Account"
import Ocean from "./ocean/Ocean"
import ServiceAgreement from "./ocean/ServiceAgreements/ServiceAgreement"
import ServiceAgreementTemplate from "./ocean/ServiceAgreements/ServiceAgreementTemplate"
import Access from "./ocean/ServiceAgreements/Templates/Access"
import FitchainCompute from "./ocean/ServiceAgreements/Templates/FitchainCompute"
import Logger from "./utils/Logger"
const Templates = {Access, FitchainCompute}
export {
Ocean,
ServiceAgreement,
ServiceAgreementTemplate,
Logger,
Templates,
Account,
}

View File

@ -1,6 +1,6 @@
import fetch from "node-fetch"
export default class AquariusConnector {
export default class WebServiceConnector {
public async post(url, payload): Promise<any> {
return this.fetch(url, {

View File

@ -0,0 +1,19 @@
import WebServiceConnector from "./WebServiceConnector"
export default class WebServiceConnectorProvider {
public static setConnector(connector: WebServiceConnector) {
WebServiceConnectorProvider.instance = connector
}
public static getConnector(): WebServiceConnector {
if (!WebServiceConnectorProvider.instance) {
WebServiceConnectorProvider.instance = new WebServiceConnector()
}
return WebServiceConnectorProvider.instance
}
private static instance: WebServiceConnector = null
}

View File

@ -1,20 +1,17 @@
import * as assert from "assert"
import Aquarius from "../../src/aquarius/Aquarius"
import AquariusConnectorProvider from "../../src/aquarius/AquariusConnectorProvider"
import SearchQuery from "../../src/aquarius/query/SearchQuery"
import DDO from "../../src/ddo/DDO"
import IdGenerator from "../../src/ocean/IdGenerator"
import WebServiceConnectorProvider from "../../src/utils/WebServiceConnectorProvider"
import config from "../config"
import AquariusConnectorMock from "../mocks/AquariusConnector.mock"
// import * as jsonDDO from "../testdata/ddo.json"
import WebServiceConnectorMock from "../mocks/WebServiceConnector.mock"
describe("Aquarius", () => {
const aquarius: Aquarius = new Aquarius(config)
describe("#queryMetadata()", () => {
it("should query metadata", async () => {
const query = {
offset: 100,
page: 0,
@ -27,20 +24,29 @@ describe("Aquarius", () => {
text: "Office",
} as SearchQuery
// @ts-ignore
AquariusConnectorProvider.setConnector(new AquariusConnectorMock())
it("should query metadata", async () => {
const result: any[] = await aquarius.queryMetadata(query)
// @ts-ignore
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock([new DDO()]))
const result: DDO[] = await aquarius.queryMetadata(query)
assert(result)
assert(result.length !== null)
})
it("should query metadata and return real ddo", async () => {
// @ts-ignore
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock([new DDO()]))
const result: DDO[] = await aquarius.queryMetadata(query)
assert(result)
assert(result[0].findServiceById)
})
})
describe("#queryMetadataByText()", () => {
it("should query metadata by text", async () => {
const query = {
offset: 100,
page: 0,
@ -53,14 +59,26 @@ describe("Aquarius", () => {
text: "Office",
} as SearchQuery
// @ts-ignore
AquariusConnectorProvider.setConnector(new AquariusConnectorMock())
it("should query metadata by text", async () => {
const result: any[] = await aquarius.queryMetadataByText(query)
// @ts-ignore
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock([new DDO()]))
const result: DDO[] = await aquarius.queryMetadataByText(query)
assert(result)
assert(result.length !== null)
})
it("should query metadata and return real ddo", async () => {
// @ts-ignore
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock([new DDO()]))
const result: DDO[] = await aquarius.queryMetadataByText(query)
assert(result)
assert(result[0].findServiceById)
})
})
describe("#storeDDO()", () => {
@ -73,7 +91,7 @@ describe("Aquarius", () => {
})
// @ts-ignore
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const result: DDO = await aquarius.storeDDO(ddo)
assert(result)
@ -91,7 +109,7 @@ describe("Aquarius", () => {
})
// @ts-ignore
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const storageResult: DDO = await aquarius.storeDDO(ddo)
assert(storageResult)

View File

@ -2,10 +2,11 @@ import Config from "../src/models/Config"
export default {
aquariusUri: "http://localhost:5000",
brizoUri: "http://localhost:3000",
nodeUri: "http://localhost:8545",
parityUri: "http://localhost:9545",
secretStoreUri: "https://secret-store.dev-ocean.com",
threshold: 2,
secretStoreUri: "http://localhost:12001",
threshold: 0,
password: "unittest",
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
web3Provider: null,

View File

@ -107,6 +107,7 @@ describe("DDO", () => {
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",
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
],
links: [
{

View File

@ -33,7 +33,7 @@ describe("Keeper", () => {
it("should get development as default", async () => {
const networkName: string = await keeper.getNetworkName()
assert(networkName === "development")
assert(networkName === "Development")
})
})

View File

@ -17,7 +17,9 @@ export default class TestContractHandler extends ContractHandler {
await TestContractHandler.deployContracts(deployerAddress)
// register templates
Logger.log(`Registering Access Template from ${deployerAddress}`)
await new ServiceAgreementTemplate(new Access()).register(deployerAddress)
Logger.log(`Registering FitchainCompute Template from ${deployerAddress}`)
await new ServiceAgreementTemplate(new FitchainCompute()).register(deployerAddress)
}
@ -78,7 +80,7 @@ export default class TestContractHandler extends ContractHandler {
// dont redeploy if there is already something loaded
if (ContractHandler.has(name)) {
return ContractHandler.get(name)
return await ContractHandler.get(name)
}
const web3 = Web3Provider.getWeb3()

View File

@ -1,8 +1,20 @@
import Aquarius from "../../src/aquarius/Aquarius"
import DDO from "../../src/ddo/DDO"
const ddoStore: Map<string, any> = new Map<string, any>()
export default class AquariusMock extends Aquarius {
public async getAccessUrl(accessToken: any, payload: any): Promise<string> {
return "http://test/test"
}
public async storeDDO(ddo: DDO): Promise<DDO> {
ddoStore.set(ddo.id, ddo)
return ddo
}
public async retrieveDDO(did: string): Promise<DDO> {
return ddoStore.get(did)
}
}

10
test/mocks/Brizo.mock.ts Normal file
View File

@ -0,0 +1,10 @@
import Brizo from "../../src/brizo/Brizo"
export default class BrizoMock extends Brizo {
public async initializeServiceAgreement(did: string, serviceAgreementId: string, serviceDefinitionId: string,
signature: string, consumerPublicKey: string): Promise<any> {
return true
}
}

View File

@ -1,7 +1,7 @@
import AquariusConnector from "../../src/aquarius/AquariusConnector"
import WebServiceConnector from "../../src/utils/WebServiceConnector"
// @ts-ignore
export default class AquariusConnectorMock extends AquariusConnector {
export default class WebServiceConnectorMock extends WebServiceConnector {
constructor(private returnData: any) {
super()
@ -14,10 +14,10 @@ export default class AquariusConnectorMock extends AquariusConnector {
resolve({
ok: true,
json: () => {
return this.returnData ? this.returnData : []
return this.returnData ? this.returnData : {}
},
text: () => {
return this.returnData ? this.returnData.toString() : ""
return this.returnData ? JSON.stringify(this.returnData.toString()) : ""
},
})
})

View File

@ -75,4 +75,14 @@ describe("Account", () => {
assert(tokensGranted === tokens)
})
})
describe("#getPublicKey()", () => {
it("should get the public key of an account", async () => {
const publicKey = await accounts[1].getPublicKey()
assert(publicKey)
assert(publicKey.length === 128)
})
})
})

View File

@ -1,16 +1,22 @@
import {assert} from "chai"
import AquariusProvider from "../../src/aquarius/AquariusProvider"
import SearchQuery from "../../src/aquarius/query/SearchQuery"
import BrizoProvider from "../../src/brizo/BrizoProvider"
import ConfigProvider from "../../src/ConfigProvider"
import DDO from "../../src/ddo/DDO"
import MetaData from "../../src/ddo/MetaData"
import Service from "../../src/ddo/Service"
import Account from "../../src/ocean/Account"
import Ocean from "../../src/ocean/Ocean"
import ServiceAgreement from "../../src/ocean/ServiceAgreements/ServiceAgreement"
import SecretStoreProvider from "../../src/secretstore/SecretStoreProvider"
import WebServiceConnectorProvider from "../../src/utils/WebServiceConnectorProvider"
import config from "../config"
import TestContractHandler from "../keeper/TestContractHandler"
import AquariusMock from "../mocks/Aquarius.mock"
import BrizoMock from "../mocks/Brizo.mock"
import SecretStoreMock from "../mocks/SecretStore.mock"
import WebServiceConnectorMock from "../mocks/WebServiceConnector.mock"
let ocean: Ocean
let accounts: Account[]
@ -21,6 +27,7 @@ describe("Ocean", () => {
before(async () => {
ConfigProvider.setConfig(config)
AquariusProvider.setAquarius(new AquariusMock(config))
BrizoProvider.setBrizo(new BrizoMock(config))
SecretStoreProvider.setSecretStore(new SecretStoreMock(config))
await TestContractHandler.prepareContracts()
ocean = await Ocean.getInstance(config)
@ -52,6 +59,21 @@ describe("Ocean", () => {
})
describe("#resolveDID()", () => {
it("should resolve a did to a ddo", async () => {
const metaData: MetaData = new MetaData()
const ddo: DDO = await ocean.registerAsset(metaData, testPublisher)
const resolvedDDO: DDO = await ocean.resolveDID(ddo.id)
assert(resolvedDDO)
assert(resolvedDDO.id === ddo.id)
})
})
describe("#registerAsset()", () => {
it("should register an asset", async () => {
@ -97,4 +119,54 @@ describe("Ocean", () => {
})
})
describe("#signServiceAgreement()", () => {
it("should sign an service agreement", async () => {
const publisher = accounts[0]
const consumer = accounts[1]
const ddo: DDO = await ocean.registerAsset(new MetaData(), publisher)
const service: Service = ddo.findServiceByType("Access")
// @ts-ignore
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const serviceAgreementSignature: any = await ocean.signServiceAgreement(ddo.id,
service.serviceDefinitionId, consumer)
assert(serviceAgreementSignature)
assert(serviceAgreementSignature.serviceAgreementId)
assert(serviceAgreementSignature.serviceAgreementSignature)
assert(serviceAgreementSignature.serviceAgreementSignature.startsWith("0x"))
})
})
describe("#executeServiceAgreement()", () => {
it("should execute an service agreement", async () => {
const publisher = accounts[0]
const consumer = accounts[1]
const ddo: DDO = await ocean.registerAsset(new MetaData(), publisher)
const service: Service = ddo.findServiceByType("Access")
// @ts-ignore
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const signServiceAgreementResult: any = await ocean.signServiceAgreement(ddo.id,
service.serviceDefinitionId, consumer)
const serviceAgreement: ServiceAgreement = await ocean.executeServiceAgreement(ddo.id,
service.serviceDefinitionId, signServiceAgreementResult.serviceAgreementId,
signServiceAgreementResult.serviceAgreementSignature, consumer, publisher)
assert(serviceAgreement)
})
})
})

View File

@ -1,10 +1,13 @@
import {assert} from "chai"
import AquariusConnectorProvider from "../../src/aquarius/AquariusConnectorProvider"
import ConfigProvider from "../../src/ConfigProvider"
import DDOCondition from "../../src/ddo/Condition"
import DDO from "../../src/ddo/DDO"
import Event from "../../src/ddo/Event"
import EventHandlers from "../../src/ddo/EventHandlers"
import MetaData from "../../src/ddo/MetaData"
import Parameter from "../../src/ddo/Parameter"
import Service from "../../src/ddo/Service"
import InputType from "../../src/models/InputType"
import Account from "../../src/ocean/Account"
import IdGenerator from "../../src/ocean/IdGenerator"
import Ocean from "../../src/ocean/Ocean"
@ -12,16 +15,20 @@ import Condition from "../../src/ocean/ServiceAgreements/Condition"
import ServiceAgreement from "../../src/ocean/ServiceAgreements/ServiceAgreement"
import ServiceAgreementTemplate from "../../src/ocean/ServiceAgreements/ServiceAgreementTemplate"
import Access from "../../src/ocean/ServiceAgreements/Templates/Access"
import WebServiceConnectorProvider from "../../src/utils/WebServiceConnectorProvider"
import config from "../config"
import TestContractHandler from "../keeper/TestContractHandler"
import AquariusConnectorMock from "../mocks/AquariusConnector.mock"
import WebServiceConnectorMock from "../mocks/WebServiceConnector.mock"
let ocean: Ocean
let accounts: Account[]
let publisherAccount: Account
let consumerAccount: Account
let serviceDefinition
let accessService: Service
let metaDataService: Service
const assetId: string = IdGenerator.generateId()
describe("ServiceAgreement", () => {
@ -34,6 +41,7 @@ describe("ServiceAgreement", () => {
publisherAccount = accounts[1]
consumerAccount = accounts[2]
const metadata = new MetaData()
const serviceAgreementTemplate: ServiceAgreementTemplate =
new ServiceAgreementTemplate(new Access())
@ -41,44 +49,108 @@ describe("ServiceAgreement", () => {
const conditions: Condition[] = await serviceAgreementTemplate.getConditions()
// create ddo conditions out of the keys
const ddoConditions: DDOCondition[] = conditions.map((condition): DDOCondition => {
const ddoConditions: DDOCondition[] = conditions.map((condition, index): DDOCondition => {
const events: Event[] = [
{
name: "PaymentReleased",
actorType: [
"consumer",
],
handlers: {
moduleName: "serviceAgreement",
functionName: "fulfillAgreement",
version: "0.1",
} as EventHandlers,
} as Event,
]
const mapParameterValueToName = (name) => {
switch (name) {
case "price":
return metadata.base.price
case "assetId":
return "0x" + assetId
case "documentKeyId":
return "0x1234"
}
return null
}
const parameters: Parameter[] = condition.methodReflection.inputs.map((input: InputType) => {
return {
name: condition.methodReflection.methodName,
timeout: condition.timeout,
conditionKey: condition.condtionKey,
parameters: condition.methodReflection.inputs.map((input) => {
return {
...input,
value: "xx",
name: input.name,
type: input.type,
value: mapParameterValueToName(input.name),
} as Parameter
}),
})
return {
contractName: condition.methodReflection.contractName,
methodName: condition.methodReflection.methodName,
timeout: condition.timeout,
index,
conditionKey: condition.condtionKey,
parameters,
events,
dependencies: condition.dependencies,
dependencyTimeoutFlags: condition.dependencyTimeoutFlags,
isTerminalCondition: condition.isTerminalCondition,
} as DDOCondition
})
serviceDefinition = [
{
accessService = {
type: "Access",
serviceDefinitionId: IdGenerator.generateId(),
templateId: serviceAgreementTemplate.getId(),
conditions: ddoConditions,
} as Service,
]
} as Service
metaDataService = {
type: "MetaData",
metadata,
} as Service
})
describe("#signServiceAgreement()", () => {
it("should sign an service agreement", async () => {
const id: string = IdGenerator.generateId()
const did: string = `did:op:${id}`
const ddo = new DDO({id: did, service: [accessService]})
const serviceAgreementId: string = IdGenerator.generateId()
// @ts-ignore
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const serviceAgreementSignature: string =
await ServiceAgreement.signServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, consumerAccount)
assert(serviceAgreementSignature)
assert(serviceAgreementSignature.startsWith("0x"))
})
})
describe("#executeServiceAgreement()", () => {
it("should execute an service agreement", async () => {
const id: string = IdGenerator.generateId()
const did: string = `did:op:${id}`
const ddo = new DDO({id: did, service: serviceDefinition})
const assetId: string = IdGenerator.generateId()
const ddo = new DDO({id: did, service: [accessService]})
const serviceAgreementId: string = IdGenerator.generateId()
// @ts-ignore
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const serviceAgreementSignature: string =
await ServiceAgreement.signServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, consumerAccount)
const serviceAgreement: ServiceAgreement =
await ServiceAgreement.signServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount,
publisherAccount)
await ServiceAgreement.executeServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, serviceAgreementSignature, consumerAccount, publisherAccount)
assert(serviceAgreement)
const serviceDefinitionId = serviceAgreement.getId()
@ -92,15 +164,19 @@ describe("ServiceAgreement", () => {
const id: string = IdGenerator.generateId()
const did: string = `did:op:${id}`
const ddo = new DDO({id: did, service: serviceDefinition})
const assetId: string = IdGenerator.generateId()
const ddo = new DDO({id: did, service: [accessService]})
const serviceAgreementId: string = IdGenerator.generateId()
// @ts-ignore
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const serviceAgreementSignature: string =
await ServiceAgreement.signServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, consumerAccount)
assert(serviceAgreementSignature)
const serviceAgreement: ServiceAgreement =
await ServiceAgreement.signServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount,
publisherAccount)
await ServiceAgreement.executeServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, serviceAgreementSignature, consumerAccount, publisherAccount)
assert(serviceAgreement)
const status = await serviceAgreement.getStatus()
@ -108,24 +184,57 @@ describe("ServiceAgreement", () => {
})
})
describe("#grantAccess()", () => {
it("should grant access in that service agreement", async () => {
describe("#lockPayment()", () => {
xit("should lock the payment in that service agreement", async () => {
const id: string = IdGenerator.generateId()
const did: string = `did:op:${id}`
const ddo = new DDO({id: did, service: serviceDefinition})
const assetId: string = IdGenerator.generateId()
const ddo = new DDO({id: did, service: [accessService, metaDataService]})
const serviceAgreementId: string = IdGenerator.generateId()
// @ts-ignore
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const serviceAgreementSignature: string =
await ServiceAgreement.signServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, consumerAccount)
assert(serviceAgreementSignature)
const serviceAgreement: ServiceAgreement =
await ServiceAgreement.signServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount,
publisherAccount)
await ServiceAgreement.executeServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, serviceAgreementSignature, consumerAccount, publisherAccount)
assert(serviceAgreement)
const fulfilled: boolean = await serviceAgreement.grantAccess(assetId, IdGenerator.generateId())
assert(fulfilled)
const paid: boolean = await serviceAgreement.lockPayment(assetId, metaDataService.metadata.base.price,
consumerAccount)
assert(paid)
})
})
describe("#grantAccess()", () => {
xit("should grant access in that service agreement", async () => {
const id: string = IdGenerator.generateId()
const did: string = `did:op:${id}`
const ddo = new DDO({id: did, service: [accessService]})
const serviceAgreementId: string = IdGenerator.generateId()
// @ts-ignore
WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo))
const serviceAgreementSignature: string =
await ServiceAgreement.signServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, consumerAccount)
assert(serviceAgreementSignature)
const serviceAgreement: ServiceAgreement =
await ServiceAgreement.executeServiceAgreement(assetId, ddo, accessService.serviceDefinitionId,
serviceAgreementId, serviceAgreementSignature, consumerAccount, publisherAccount)
assert(serviceAgreement)
const paid: boolean = await serviceAgreement.lockPayment(assetId, 10, consumerAccount)
assert(paid)
const accessGranted: boolean = await serviceAgreement.grantAccess(assetId, IdGenerator.generateId())
assert(accessGranted)
})
})
})

View File

@ -39,6 +39,20 @@ describe("ServiceAgreementTemplate", () => {
})
})
describe("#getConditions()", () => {
it("should setup an Access agreement template correctly", async () => {
const access: TemplateBase = new Access()
access.id = IdGenerator.generatePrefixedId()
const serviceAgreementTemplate: ServiceAgreementTemplate =
new ServiceAgreementTemplate(access)
assert(serviceAgreementTemplate)
const conds = await serviceAgreementTemplate.getConditions()
assert(conds)
})
})
describe("#getStatus()", () => {
it("should get the status of a newly deployed agreement template", async () => {

34
test/testdata/MetaData.ts vendored Normal file
View File

@ -0,0 +1,34 @@
import MetaDataModel from "../../src/ddo/MetaData"
import MetaDataBase from "../../src/ddo/MetaDataBase"
const MetaData = new MetaDataModel({
base: {
name: "Office Humidity",
type: "dataset",
description: "Weather information of UK including temperature and humidity",
size: "3.1gb",
dateCreated: "2012-02-01T10:55:11+00:00",
author: "Met Office",
license: "CC-BY",
copyrightHolder: "Met Office",
encoding: "UTF-8",
compression: "zip",
contentType: "text/csv",
// tslint:disable-next-line
workExample: "stationId,latitude,longitude,datetime,temperature,humidity423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
contentUrls: [
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
],
links: [
{sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/"},
{sample2: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/"},
{fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/"},
],
inLanguage: "en",
tags: "weather, uk, 2011, temperature, humidity",
price: 10,
} as MetaDataBase,
} as MetaDataModel)
export default MetaData