diff --git a/package-lock.json b/package-lock.json index 1d3e41c..2934fc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -120,9 +120,9 @@ } }, "@oceanprotocol/keeper-contracts": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@oceanprotocol/keeper-contracts/-/keeper-contracts-0.3.11.tgz", - "integrity": "sha512-4xJtXc77Rw1pO9yt1NryFypx3KOzQcKX6Iv7c5dKXIh+FigSmrOPgVFtbFj0bwtLwpmt2+2JIYd/iiN943q/fw==" + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@oceanprotocol/keeper-contracts/-/keeper-contracts-0.3.12.tgz", + "integrity": "sha512-mJAldSLxyAhfhEI0IB5o3HVLGeZ65dz69331COPrsOoEsy288bhX1QMwdJS4B1C5nO3cvf41Xgp8qgCpTouNvA==" }, "@oceanprotocol/secret-store-client": { "version": "0.0.7", diff --git a/package.json b/package.json index f4bc350..0aece4b 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build": "npm run clean && npm run lint && tsc && npm run doc", "build:watch": "tsc -w", "doc": "typedoc --mode modules --out ./doc/ ./src/", + "run": "ts-node", "release": "./node_modules/release-it/bin/release-it.js --src.tagName='v%s' --github.release --npm.publish --non-interactive", "release-minor": "./node_modules/release-it/bin/release-it.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive", "release-major": "./node_modules/release-it/bin/release-it.js major --src.tagName='v%s' --github.release --npm.publish --non-interactive", @@ -50,8 +51,8 @@ "node": ">=8 <10" }, "dependencies": { - "@oceanprotocol/keeper-contracts": "^0.3.11", - "@oceanprotocol/secret-store-client": "^0.0.7", + "@oceanprotocol/keeper-contracts": "^0.3.12", + "@oceanprotocol/secret-store-client": "0.0.7", "bignumber.js": "^8.0.1", "eth-crypto": "^1.2.5", "eth-ecies": "^1.0.3", diff --git a/src/ddo/Condition.ts b/src/ddo/Condition.ts new file mode 100644 index 0000000..f6f398f --- /dev/null +++ b/src/ddo/Condition.ts @@ -0,0 +1,6 @@ +export default class Condition { + public name: string = "lockPayment" + public timeout: number = 0 + public conditionKey: string + public parameters: any +} diff --git a/src/ddo/Service.ts b/src/ddo/Service.ts index b02192c..7e910b5 100644 --- a/src/ddo/Service.ts +++ b/src/ddo/Service.ts @@ -1,8 +1,13 @@ +import Condition from "./Condition" import MetaData from "./MetaData" export default class Service { public type: string = "OpenIdConnectVersion1.0Service" + public serviceDefinitionId?: string + public templateId?: string public serviceEndpoint: string = "https://openid.example.com/" + public purchaseEndpoint?: string public description?: string = "My public social inbox" public metadata?: MetaData = {} as MetaData + public conditions?: Condition[] = [] } diff --git a/src/examples/GrantAccess.ts b/src/examples/GrantAccess.ts new file mode 100644 index 0000000..d6857f4 --- /dev/null +++ b/src/examples/GrantAccess.ts @@ -0,0 +1,33 @@ +import ServiceAgreement from "../ocean/ServiceAgreement" +import {Account, Asset, 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 consumer: Account = (await ocean.getAccounts())[0] + + const asset: Asset = new Asset( + "Fancy Car Data", + "nice data", + 100, publisher) + + const assetDid = await ocean.register(asset) + Logger.log("asset did:", assetDid) + + const serviceAgreement: ServiceAgreement = await asset.purchase(consumer) + Logger.log("service defintion id:", serviceAgreement.getId()) + + const accessGranted: boolean = + await serviceAgreement.grantAccess(asset.getId(), "321721938712931283") + + Logger.log("access granted:", accessGranted) +})() diff --git a/src/examples/Purchase.ts b/src/examples/Purchase.ts new file mode 100644 index 0000000..e81fc50 --- /dev/null +++ b/src/examples/Purchase.ts @@ -0,0 +1,28 @@ +import {Account, Asset, Logger, Ocean} from "../squid" +import ServiceAgreement from "../ocean/ServiceAgreement" + +(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 consumer: Account = (await ocean.getAccounts())[1] + + const asset: Asset = new Asset( + "Fancy Car Data", + "nice data", + 100, publisher) + + const assetDid = await ocean.register(asset) + Logger.log(assetDid) + + const serviceAgreement: ServiceAgreement = await asset.purchase(consumer) + Logger.log(serviceAgreement.getId()) +})() diff --git a/src/examples/Register.ts b/src/examples/Register.ts new file mode 100644 index 0000000..6a255a9 --- /dev/null +++ b/src/examples/Register.ts @@ -0,0 +1,22 @@ +import {Account, Asset, 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 asset: Asset = new Asset( + "Fancy Car Data", + "nice data", 100, + publisher) + const assetDid = await ocean.register(asset) + + Logger.log(assetDid) +})() diff --git a/src/keeper/contracts/ServiceAgreement.ts b/src/keeper/contracts/ServiceAgreement.ts index 4eaa7dd..2d7f0a3 100644 --- a/src/keeper/contracts/ServiceAgreement.ts +++ b/src/keeper/contracts/ServiceAgreement.ts @@ -24,18 +24,19 @@ export default class ServiceAgreement extends ContractBase { return this.call("getTemplateStatus", [templateId]) } - public async getAgreementStatus(serviceAgreementId: string) { + public async getAgreementStatus(serviceDefinitionId: string) { - return this.call("getAgreementStatus", ["0x" + serviceAgreementId]) + return this.call("getAgreementStatus", [serviceDefinitionId]) } public async executeAgreement(serviceAgreementTemplateId: string, serviceAgreementSignatureHash: string, consumerAddress: string, valueHashes: string[], timeoutValues: number[], - serviceAgreementId: string, did: string, publisherAddress: string): Promise { + serviceAgreementId: string, did: string, publisherAddress: string): + Promise { return this.send("executeAgreement", publisherAddress, [ serviceAgreementTemplateId, serviceAgreementSignatureHash, consumerAddress, valueHashes, - timeoutValues, "0x" + serviceAgreementId, "0x" + did, + timeoutValues, "0x" + serviceAgreementId, "0x" + did.replace("did:op:", ""), ]) } } diff --git a/src/keeper/contracts/conditions/AccessConditions.ts b/src/keeper/contracts/conditions/AccessConditions.ts index 1d4c790..bed38d6 100644 --- a/src/keeper/contracts/conditions/AccessConditions.ts +++ b/src/keeper/contracts/conditions/AccessConditions.ts @@ -9,10 +9,10 @@ export default class AccessConditions extends ContractBase { return accessConditions } - public async grantAccess(serviceAgreementId: any, assetId: any, documentKeyId: any, publisherAddress: string) + public async grantAccess(serviceDefinitionId: any, assetId: any, documentKeyId: any, publisherAddress: string) : Promise { return this.send("grantAccess", publisherAddress, [ - "0x" + serviceAgreementId, "0x" + assetId, "0x" + documentKeyId, + serviceDefinitionId, "0x" + assetId, "0x" + documentKeyId, ]) } } diff --git a/src/models/Config.ts b/src/models/Config.ts index 1de88c6..7b8cc71 100644 --- a/src/models/Config.ts +++ b/src/models/Config.ts @@ -5,9 +5,9 @@ export default class Config { /* Keeper Config */ // the uri to the node we want to connect to, not need if web3Provider is set - public nodeUri: string + public nodeUri?: string // from outside eg. metamask - public web3Provider: any + public web3Provider?: any /* Secret Store Config */ // the uri of the secret store to connect to diff --git a/src/ocean/Asset.ts b/src/ocean/Asset.ts index 5b43f42..02169f5 100644 --- a/src/ocean/Asset.ts +++ b/src/ocean/Asset.ts @@ -1,10 +1,8 @@ -import * as EthCrypto from "eth-crypto" -import * as EthjsUtil from "ethereumjs-util" -import Keeper from "../keeper/Keeper" -import Logger from "../utils/Logger" +import AquariusProvider from "../aquarius/AquariusProvider" import Account from "./Account" +import IdGenerator from "./IdGenerator" import OceanBase from "./OceanBase" -import Order from "./Order" +import ServiceAgreement from "./ServiceAgreement" export default class Asset extends OceanBase { @@ -15,42 +13,15 @@ export default class Asset extends OceanBase { super() } - public async purchase(consumer: Account, timeout: number): Promise { - const {token, market, auth} = await Keeper.getInstance() + public async purchase(consumer: Account): Promise { - const key = EthCrypto.createIdentity() - const publicKey = EthjsUtil.privateToPublic(key.privateKey).toString("hex") - const price = await market.getAssetPrice(this.getId()) - const isValid = await market.isAssetActive(this.getId()) + const did = `did:op:${this.getId()}` + const ddo = await AquariusProvider.getAquarius().retrieveDDO(did) - Logger.log("The asset:", this.getId(), "is it valid?", isValid, "it's price is:", price) + const serviceAgreementId: string = IdGenerator.generateId() + const serviceAgreement: ServiceAgreement = await ServiceAgreement.createServiceAgreement(this.getId(), + ddo, serviceAgreementId, consumer, this.publisher) - if (!isValid) { - throw new Error("The Asset is not valid!") - } - try { - const marketAddr = market.getAddress() - // Allow market contract to transfer funds on the consumer"s behalf - await token.approve(marketAddr, price, consumer.getId()) - Logger.log(`${price} tokens approved on market with id: ${marketAddr}`) - } catch (err) { - Logger.error("token.approve failed", err) - } - let order: Order - try { - // Submit the access request - const initiateAccessRequestReceipt = await auth.initiateAccessRequest(this, - publicKey, timeout, consumer.getId()) - - const {returnValues} = initiateAccessRequestReceipt.events.AccessConsentRequested - Logger.log(`Keeper AccessConsentRequested event received on asset: ${this.getId()}`) - order = new Order(this, returnValues._timeout, returnValues._pubKey, key) - order.setId(returnValues._id) - } catch (err) { - Logger.error("auth.initiateAccessRequest failed", err) - } - - return order + return serviceAgreement } - } diff --git a/src/ocean/Ocean.ts b/src/ocean/Ocean.ts index dcd275b..eb40cd9 100644 --- a/src/ocean/Ocean.ts +++ b/src/ocean/Ocean.ts @@ -1,14 +1,16 @@ import AquariusProvider from "../aquarius/AquariusProvider" +import SearchQuery from "../aquarius/query/SearchQuery" import ConfigProvider from "../ConfigProvider" +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 Config from "../models/Config" -import SecretStoreProvider from "../secretstore/SecretStoreProvider" -import Logger from "../utils/Logger" import Account from "./Account" import Asset from "./Asset" import IdGenerator from "./IdGenerator" -import Order from "./Order" +import ServiceAgreementTemplate from "./ServiceAgreementTemplate" export default class Ocean { @@ -37,71 +39,71 @@ export default class Ocean { return ethAccounts.map((address: string) => new Account(address)) } - public async register(asset: Asset): Promise { + public async register(asset: Asset): Promise { + const {market} = this.keeper - const secretStore = SecretStoreProvider.getSecretStore() + const assetId: string = IdGenerator.generateId() + const did: string = `did:op:${assetId}` - const assetId = IdGenerator.generateId() + const methods: string[] = [ + "PaymentConditions.lockPayment", + "AccessConditions.grantAccess", + "PaymentConditions.releasePayment", + "PaymentConditions.refundPayment", + ] + // tslint:disable + const dependencyMatrix = [0, 1, 4, 1 | 2 ** 4 | 2 ** 5] // dependency bit | timeout bit - const encryptedDocument = await secretStore.encryptDocument(assetId, asset) + const serviceName = "Access" + const saTemplate: ServiceAgreementTemplate = + await ServiceAgreementTemplate.registerServiceAgreementsTemplate(serviceName, methods, dependencyMatrix, + asset.publisher) - const decryptedDocument = await secretStore.decryptDocument(assetId, encryptedDocument) + // get condition keys from template + const conditionKeys: string[] = saTemplate.getConditionKeys() - Logger.log(decryptedDocument, encryptedDocument) - - // generate an id - Logger.log(`Registering: ${assetId} with price ${asset.price} for ${asset.publisher.getId()}`) - asset.setId(assetId) - const isAssetActive = await market.isAssetActive(assetId) - // register asset in the market - if (!isAssetActive) { - const result = await market.register(asset.getId(), asset.price, asset.publisher.getId()) - Logger.log("Registered:", assetId, "in block", result.blockNumber) - } else { - throw new Error("Asset already registered") - } - return assetId - } - - public async getOrdersByAccount(consumer: Account): Promise { - const {auth} = this.keeper - - Logger.log("Getting orders") - - const accessConsentRequestedData = await auth.getEventData( - "AccessConsentRequested", { - filter: { - _consumer: consumer.getId(), + // create ddo conditions out of the keys + const conditions: Condition[] = conditionKeys.map((conditionKey, i): Condition => { + return { + name: methods[i].split(".")[1], + timeout: 100, + conditionKey: conditionKey, + parameters: { + // todo wtf? + assetId: "bytes32", + price: "integer" }, - fromBlock: 0, - toBlock: "latest", - }) + } as Condition + }) - const orders = await Promise.all( - accessConsentRequestedData - .map(async (event: any) => { + // create ddo itself + const ddo: DDO = new DDO({ + id: did, + service: [ + { + type: serviceName, + // tslint:disable + 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?", + // the id of the service agreement? + serviceDefinitionId: "0x" + IdGenerator.generateId(), + // the id of the service agreement template + templateId: saTemplate.getId(), + conditions, + } as Service, + ], + }) - const {returnValues} = event - const order: Order = new Order( - null, - parseInt(returnValues._timeout, 10), - null, null) + await AquariusProvider.getAquarius().storeDDO(ddo) + asset.setId(assetId) - order.setId(returnValues._id) - - return order - }), - ) - - // Logger.log("Got orders:", JSON.stringify(orders, null, 2)) - Logger.log(`Got ${Object.keys(orders).length} orders`) - - return orders + await market.register(assetId, asset.price, asset.publisher.getId()) + return ddo } - public async searchAssets(query): Promise { + public async searchAssets(query: SearchQuery): Promise { return AquariusProvider.getAquarius().queryMetadata(query) } } diff --git a/src/ocean/ServiceAgreement.ts b/src/ocean/ServiceAgreement.ts index e836781..f621cec 100644 --- a/src/ocean/ServiceAgreement.ts +++ b/src/ocean/ServiceAgreement.ts @@ -1,57 +1,86 @@ +import DDO from "../ddo/DDO" import AccessConditions from "../keeper/contracts/conditions/AccessConditions" import ServiceAgreementContract from "../keeper/contracts/ServiceAgreement" import Web3Provider from "../keeper/Web3Provider" import Account from "./Account" -import IdGenerator from "./IdGenerator" import OceanBase from "./OceanBase" -import ServiceAgreementTemplate from "./ServiceAgreementTemplate" export default class ServiceAgreement extends OceanBase { - public static async createServiceAgreement(serviceAgreementTemplate: ServiceAgreementTemplate, assetId: string, - did: string, consumer: Account, publisher: Account) { + public static async createServiceAgreement(assetId: string, ddo: DDO, serviceAgreementId: string, consumer: Account, + publisher: Account): + Promise { + + const timeoutValues: number[] = ddo.service[0].conditions.map((condition) => { + return condition.timeout + }) // todo: this should come from ddo - const serviceAgreementId = IdGenerator.generateId() - const timeoutValues = [0, 0, 0, 500] // timeout 500 blocks @ condition 4 const values = [ {type: "bool", value: true}, {type: "bool", value: false}, {type: "uint", value: 120}, - {type: "string", value: assetId}, + {type: "string", value: serviceAgreementId}, ] - const saHashSig = await ServiceAgreement.createSAHashSignature(serviceAgreementTemplate, serviceAgreementId, - values, timeoutValues, consumer) + const serviceAgreementHashSignature = await ServiceAgreement.createSAHashSignature(ddo, serviceAgreementId, + consumer) - const serviceAgreement: ServiceAgreement = await ServiceAgreement.signServiceAgreement(serviceAgreementTemplate, - serviceAgreementId, assetId, did, values, timeoutValues, saHashSig, consumer, publisher) + const serviceAgreement: ServiceAgreement = await ServiceAgreement.signServiceAgreement(ddo, + serviceAgreementId, values, timeoutValues, serviceAgreementHashSignature, consumer, publisher) return serviceAgreement } - private static async signServiceAgreement(serviceAgreementTemplate: ServiceAgreementTemplate, - serviceAgreementId: string, assetId: string, did: string, values: any[], + public static async createSAHashSignature(ddo: DDO, serviceAgreementId: string, consumer: Account): + Promise { + + // todo get from ddo + const values = [ + {type: "bool", value: true}, + {type: "bool", value: false}, + {type: "uint", value: 120}, + {type: "string", value: serviceAgreementId}, + ] + + const valueHashes = ServiceAgreement.createValueHashes(values) + + const conditionKeys: string[] = ddo.service[0].conditions.map((condition) => { + return condition.conditionKey + }) + + const timeoutValues: number[] = ddo.service[0].conditions.map((condition) => { + return condition.timeout + }) + + const serviceAgreementHash = ServiceAgreement.hashServiceAgreement(ddo.service[0].templateId, + serviceAgreementId, conditionKeys, valueHashes, timeoutValues) + + const serviceAgreementHashSignature = + await Web3Provider.getWeb3().eth.sign(serviceAgreementHash, consumer.getId()) + + return serviceAgreementHashSignature + } + + private static async signServiceAgreement(ddo: DDO, serviceAgreementId: string, values: any[], timeoutValues: number[], serviceAgreementHashSignature: string, consumer: Account, publisher: Account): Promise { const valueHashes = ServiceAgreement.createValueHashes(values) - const serviceAgreement: ServiceAgreementContract = await ServiceAgreementContract.getInstance() - const executeAgreementReceipt = await serviceAgreement.executeAgreement( - serviceAgreementTemplate.getId(), serviceAgreementHashSignature, consumer.getId(), valueHashes, - timeoutValues, serviceAgreementId, did, publisher.getId()) + ddo.service[0].templateId, serviceAgreementHashSignature, consumer.getId(), valueHashes, + timeoutValues, serviceAgreementId, ddo.id, publisher.getId()) if (executeAgreementReceipt.events.ExecuteAgreement.returnValues.state === false) { throw new Error("signing service agreement failed.") } return new ServiceAgreement( - serviceAgreementId, + executeAgreementReceipt.events.ExecuteAgreement.returnValues.serviceAgreementId, + ddo, publisher, - serviceAgreementTemplate, consumer, executeAgreementReceipt.events.ExecuteAgreement.returnValues.state, executeAgreementReceipt.events.ExecuteAgreement.returnValues.status, @@ -69,43 +98,30 @@ export default class ServiceAgreement extends OceanBase { return Web3Provider.getWeb3().utils.soliditySha3(args).toString("hex") } - private static async createSAHashSignature(serviceAgreementTemplate: ServiceAgreementTemplate, - serviceAgreementId: string, values: any[], timeoutValues: number[], - consumer: Account): Promise { - - const valueHashes = ServiceAgreement.createValueHashes(values) - - const serviceAgreementHash = ServiceAgreement.hashServiceAgreement(serviceAgreementTemplate, valueHashes, - timeoutValues, serviceAgreementId) - const serviceAgreementHashSignature = - await Web3Provider.getWeb3().eth.sign(serviceAgreementHash, consumer.getId()) - - return serviceAgreementHashSignature - } - - private static hashServiceAgreement(serviceAgreementTemplate: ServiceAgreementTemplate, valueHashes: string[], - timeouts: number[], serviceAgreementId: string) { + private static hashServiceAgreement(serviceAgreementTemplateId: string, serviceAgreementId: string, + conditionKeys: string[], valueHashes: string[], timeouts: number[]) { const args = [ - {type: "bytes32", value: serviceAgreementTemplate.getId()}, - {type: "bytes32[]", value: serviceAgreementTemplate.getConditionKeys()}, + {type: "bytes32", value: serviceAgreementTemplateId}, + {type: "bytes32[]", value: conditionKeys}, {type: "bytes32[]", value: valueHashes}, {type: "uint256[]", value: timeouts}, - {type: "bytes32", value: "0x" + serviceAgreementId}, + {type: "bytes32", value: serviceAgreementId}, ] return Web3Provider.getWeb3().utils.soliditySha3(...args).toString("hex") } - private constructor(id: string, private publisher: Account, serviceAgreementTemplate: ServiceAgreementTemplate, - consumer: Account, state: boolean, status: boolean) { - super(id) + private constructor(serviceAgreementId: string, ddo: DDO, private publisher: Account, consumer: Account, + state: boolean, status: boolean) { + super(serviceAgreementId) } public async grantAccess(assetId: string, documentId: string): Promise { const accessConditions: AccessConditions = await AccessConditions.getInstance() const grantAccessReceipt = - await accessConditions.grantAccess(this.getId(), assetId, documentId, this.publisher.getId()) + await accessConditions.grantAccess(this.getId(), assetId, documentId, + this.publisher.getId()) return grantAccessReceipt.status } diff --git a/src/ocean/ServiceAgreementTemplate.ts b/src/ocean/ServiceAgreementTemplate.ts index c0ac0aa..e2d2e26 100644 --- a/src/ocean/ServiceAgreementTemplate.ts +++ b/src/ocean/ServiceAgreementTemplate.ts @@ -25,11 +25,12 @@ export default class ServiceAgreementTemplate extends OceanBase { Web3Provider.getWeb3().utils.fromAscii(serviceName), templateOwner.getId()) - const id = receipt.events.SetupAgreementTemplate.returnValues.serviceTemplateId + const serviceAgreementTemplateId = + receipt.events.SetupAgreementTemplate.returnValues.serviceTemplateId return new ServiceAgreementTemplate( - id, - ServiceAgreementTemplate.generateConditionsKeys(id, methodReflections), + serviceAgreementTemplateId, + ServiceAgreementTemplate.generateConditionsKeys(serviceAgreementTemplateId, methodReflections), templateOwner) } @@ -47,17 +48,15 @@ export default class ServiceAgreementTemplate extends OceanBase { return conditions } - private constructor(id, private conditionKeys: string[], private owner: Account) { - super(id) + private constructor(serviceAgreementTemplateId, private conditionKeys: string[], private owner: Account) { + super(serviceAgreementTemplateId) } /** * gets the status of a service agreement template */ public async getStatus(): Promise { - const serviceAgreement: ServiceAgreement = await ServiceAgreement.getInstance() - return serviceAgreement.getTemplateStatus(this.getId()) } diff --git a/src/secretstore/SecretStoreProvider.ts b/src/secretstore/SecretStoreProvider.ts index 45007cb..da3d103 100644 --- a/src/secretstore/SecretStoreProvider.ts +++ b/src/secretstore/SecretStoreProvider.ts @@ -8,7 +8,7 @@ export default class SecretStoreProvider { SecretStoreProvider.secretStore = secretStore } - public static getSecretStore() { + public static getSecretStore(): SecretStore { if (!SecretStoreProvider.secretStore) { SecretStoreProvider.secretStore = new SecretStore(ConfigProvider.getConfig()) diff --git a/src/squid.ts b/src/squid.ts index 3410c4c..6ef2c31 100644 --- a/src/squid.ts +++ b/src/squid.ts @@ -1,3 +1,4 @@ +import Account from "./ocean/Account" import Asset from "./ocean/Asset" import Ocean from "./ocean/Ocean" import Order from "./ocean/Order" @@ -8,4 +9,5 @@ export { Order, Asset, Logger, + Account, } diff --git a/test/ocean/Asset.test.ts b/test/ocean/Asset.test.ts index 5118184..747474f 100644 --- a/test/ocean/Asset.test.ts +++ b/test/ocean/Asset.test.ts @@ -1,25 +1,28 @@ import {assert} from "chai" +import AquariusConnectorProvider from "../../src/aquarius/AquariusConnectorProvider" import AquariusProvider from "../../src/aquarius/AquariusProvider" import ConfigProvider from "../../src/ConfigProvider" +import DDO from "../../src/ddo/DDO" import ContractHandler from "../../src/keeper/ContractHandler" import Account from "../../src/ocean/Account" import Asset from "../../src/ocean/Asset" import Ocean from "../../src/ocean/Ocean" -import Order from "../../src/ocean/Order" +import ServiceAgreement from "../../src/ocean/ServiceAgreement" import SecretStoreProvider from "../../src/secretstore/SecretStoreProvider" import config from "../config" import AquariusMock from "../mocks/Aquarius.mock" +import AquariusConnectorMock from "../mocks/AquariusConnector.mock" import SecretStoreMock from "../mocks/SecretStore.mock" const testName = "Test Asset 2" const testDescription = "This asset is pure owange" const testPrice = 100 -const timeout = 100000 let ocean: Ocean let testAsset: Asset let accounts: Account[] let testPublisher: Account +let ddo: DDO describe("Asset", () => { @@ -33,17 +36,30 @@ describe("Asset", () => { testPublisher = accounts[0] testAsset = new Asset(testName, testDescription, testPrice, testPublisher) - await ocean.register(testAsset) + ddo = await ocean.register(testAsset) + + // @ts-ignore + AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo)) }) describe("#purchase()", () => { it("should purchase an asset", async () => { - // todo const consumerAccount = accounts[5] - const order: Order = await testAsset.purchase(consumerAccount, timeout) - assert(order) + const serviceAgreement: ServiceAgreement = await testAsset.purchase(consumerAccount) + assert(serviceAgreement) + }) + + it("should purchase an asset from two different customers", async () => { + + const consumerAccount1 = accounts[5] + const serviceAgreement1: ServiceAgreement = await testAsset.purchase(consumerAccount1) + assert(serviceAgreement1) + + const consumerAccount2 = accounts[6] + const serviceAgreement2: ServiceAgreement = await testAsset.purchase(consumerAccount2) + assert(serviceAgreement2) }) }) }) diff --git a/test/ocean/Ocean.test.ts b/test/ocean/Ocean.test.ts index a7ef21e..8f20298 100644 --- a/test/ocean/Ocean.test.ts +++ b/test/ocean/Ocean.test.ts @@ -1,11 +1,12 @@ import {assert} from "chai" import AquariusProvider from "../../src/aquarius/AquariusProvider" +import SearchQuery from "../../src/aquarius/query/SearchQuery" import ConfigProvider from "../../src/ConfigProvider" +import DDO from "../../src/ddo/DDO" import ContractHandler from "../../src/keeper/ContractHandler" import Account from "../../src/ocean/Account" import Asset from "../../src/ocean/Asset" import Ocean from "../../src/ocean/Ocean" -import Order from "../../src/ocean/Order" import SecretStoreProvider from "../../src/secretstore/SecretStoreProvider" import config from "../config" import AquariusMock from "../mocks/Aquarius.mock" @@ -13,13 +14,11 @@ import SecretStoreMock from "../mocks/SecretStore.mock" let ocean: Ocean let accounts: Account[] -let testAsset: Asset let testPublisher: Account -const name = "Test Asset 3" +const name = "Test Asset 3" + Math.random().toString() const description = "This asset is pure owange" const price = 100 -const timeout = 100000000 describe("Ocean", () => { @@ -32,18 +31,16 @@ describe("Ocean", () => { accounts = await ocean.getAccounts() testPublisher = accounts[0] - testAsset = new Asset(name, description, price, testPublisher) }) describe("#getInstance()", () => { - it("should list accounts", async () => { + it("should get an instance of cean", async () => { - const ocn = Ocean.getInstance(config) + const oceanInstance: Ocean = await Ocean.getInstance(config) - assert(ocn) + assert(oceanInstance) }) - }) describe("#getAccounts()", () => { @@ -63,31 +60,13 @@ describe("Ocean", () => { it("should register an asset", async () => { - const assetId: string = await ocean.register(testAsset) + const asset: Asset = new Asset(name, description, price, testPublisher) + const ddo: DDO = await ocean.register(asset) - assert(assetId.length === 64) - assert(!assetId.startsWith("0x")) + assert(ddo.id.startsWith("did:op:")) }) }) - describe("#getOrdersByConsumer()", () => { - - it("should list orders", async () => { - - const testConsumer = accounts[1] - const asset: Asset = new Asset("getOrdersByConsumer test", description, price, testPublisher) - - await ocean.register(asset) - - const order: Order = await asset.purchase(testConsumer, timeout) - const orders = await ocean.getOrdersByAccount(testConsumer) - - assert(orders.length === 1) - assert(orders[0].getId() === order.getId()) - }) - - }) - describe("#searchAssets()", () => { it("should search for assets", async () => { @@ -102,7 +81,7 @@ describe("Ocean", () => { value: 1, }, text: "Office", - } + } as SearchQuery const assets: any[] = await ocean.searchAssets(query) diff --git a/test/ocean/Order.test.ts b/test/ocean/Order.test.ts deleted file mode 100644 index 75b0136..0000000 --- a/test/ocean/Order.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import {assert} from "chai" -import AquariusProvider from "../../src/aquarius/AquariusProvider" -import ConfigProvider from "../../src/ConfigProvider" -import ContractHandler from "../../src/keeper/ContractHandler" -import AccessStatus from "../../src/models/AccessStatus" -import Account from "../../src/ocean/Account" -import Asset from "../../src/ocean/Asset" -import Ocean from "../../src/ocean/Ocean" -import Order from "../../src/ocean/Order" -import config from "../config" -import AquariusMock from "../mocks/Aquarius.mock" -import * as AccessToken from "../testdata/AccessToken.json" - -const testName = "Order Test Asset" -const testDescription = "This asset is pure owange" -const testPrice = 100 -const timeout = 1000000 - -let ocean: Ocean -let testAsset: Asset -let accounts: Account[] -let testPublisher: Account -let testConsumer: Account - -describe("Order", () => { - - before(async () => { - ConfigProvider.setConfig(config) - AquariusProvider.setAquarius(new AquariusMock(config)) - await ContractHandler.deployContracts() - ocean = await Ocean.getInstance(config) - accounts = await ocean.getAccounts() - testPublisher = accounts[0] - testConsumer = accounts[1] - // register an asset to play around with - testAsset = new Asset(testName, testDescription, testPrice, testPublisher) - await ocean.register(testAsset) - }) - - describe("#pay()", async () => { - - it("should pay for an order", async () => { - - const order: Order = await testAsset.purchase(testConsumer, timeout) - assert(order) - - await order.commit(AccessToken.toString()) - await testConsumer.requestTokens(testAsset.price) - const paymentId: string = await order.pay(testConsumer) - assert(paymentId) - }) - - }) - - describe("#commit()", async () => { - - it("should commit the order", async () => { - - const order: Order = await testAsset.purchase(testConsumer, timeout) - assert(order) - - await order.commit(AccessToken.toString()) - }) - }) - - describe("#getStatus()", async () => { - - it("should get status Requested on new order", async () => { - - const order: Order = await testAsset.purchase(testConsumer, timeout) - assert(order) - - const status: AccessStatus = await order.getStatus() - assert(status === AccessStatus.Requested) - }) - - it("should get status Delivered on commited order", async () => { - - const order: Order = await testAsset.purchase(testConsumer, timeout) - assert(order) - - await order.commit(AccessToken.toString()) - - const status: AccessStatus = await order.getStatus() - assert(status === AccessStatus.Delivered) - }) - }) - - describe("#consume()", () => { - - it("should consume an asset", async () => { - const consumerAccount = accounts[5] - await consumerAccount.requestTokens(testAsset.price) - // place order - consumer - const order: Order = await testAsset.purchase(consumerAccount, timeout) - // commit order - provider - await order.commit(AccessToken.toString()) - // pay order - consumer - await order.pay(consumerAccount) - const url = await order.consume(consumerAccount) - assert(url) - }) - }) -}) diff --git a/test/ocean/ServiceAgreement.test.ts b/test/ocean/ServiceAgreement.test.ts index 15ed40d..51e6071 100644 --- a/test/ocean/ServiceAgreement.test.ts +++ b/test/ocean/ServiceAgreement.test.ts @@ -1,5 +1,7 @@ import {assert} from "chai" +import AquariusConnectorProvider from "../../src/aquarius/AquariusConnectorProvider" import ConfigProvider from "../../src/ConfigProvider" +import DDO from "../../src/ddo/DDO" import ContractHandler from "../../src/keeper/ContractHandler" import Account from "../../src/ocean/Account" import IdGenerator from "../../src/ocean/IdGenerator" @@ -7,6 +9,9 @@ import Ocean from "../../src/ocean/Ocean" import ServiceAgreement from "../../src/ocean/ServiceAgreement" import ServiceAgreementTemplate from "../../src/ocean/ServiceAgreementTemplate" import config from "../config" +import AquariusConnectorMock from "../mocks/AquariusConnector.mock" +import Service from "../../src/ddo/Service" +import Condition from "../../src/ddo/Condition" let ocean: Ocean let accounts: Account[] @@ -15,6 +20,7 @@ let templateOwnerAccount: Account let consumerAccount: Account let testServiceAgreementTemplate: ServiceAgreementTemplate +let serviceDefintion describe("ServiceAgreement", () => { @@ -35,42 +41,79 @@ describe("ServiceAgreement", () => { "PaymentConditions.releasePayment", "PaymentConditions.refundPayment", ] + // tslint:disable const dependencyMatrix = [0, 1, 4, 1 | 2 ** 4 | 2 ** 5] // dependency bit | timeout bit testServiceAgreementTemplate = await ServiceAgreementTemplate.registerServiceAgreementsTemplate(resourceName, methods, dependencyMatrix, templateOwnerAccount) + + // get condition keys from template + const conditionKeys: string[] = testServiceAgreementTemplate.getConditionKeys() + + // create ddo conditions out of the keys + const conditions: Condition[] = conditionKeys.map((conditionKey, i): Condition => { + return { + name: methods[i].split(".")[1], + timeout: 100, + conditionKey: conditionKey, + parameters: { + // todo wtf? + assetId: "bytes32", + price: "integer" + }, + } as Condition + }) + + serviceDefintion = [ + { + serviceDefinitionId: IdGenerator.generateId(), + templateId: testServiceAgreementTemplate.getId(), + conditions, + } as Service + ] + }) describe("#createServiceAgreement()", () => { it("should execute an service agreement", async () => { - const did: string = IdGenerator.generateId() + const id: string = IdGenerator.generateId() + const did: string = `did:op:${id}` + const ddo = new DDO({id: did, service: serviceDefintion}) const assetId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generateId() + // @ts-ignore + AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo)) const serviceAgreement: ServiceAgreement = - await ServiceAgreement.createServiceAgreement(testServiceAgreementTemplate, assetId, did, - consumerAccount, publisherAccount) - + await ServiceAgreement.createServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount, + publisherAccount) assert(serviceAgreement) - const id = serviceAgreement.getId() - assert(id) - assert(id !== did) + + const serviceDefinitionId = serviceAgreement.getId() + assert(serviceDefinitionId) + assert(serviceDefinitionId !== did) }) }) describe("#getStatus()", () => { it("should get the status of a newly created service agreement", async () => { - const did: string = IdGenerator.generateId() + const id: string = IdGenerator.generateId() + const did: string = `did:op:${id}` + const ddo = new DDO({id: did, service: serviceDefintion}) const assetId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generateId() + // @ts-ignore + AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo)) const serviceAgreement: ServiceAgreement = - await ServiceAgreement.createServiceAgreement(testServiceAgreementTemplate, assetId, did, - consumerAccount, publisherAccount) - + await ServiceAgreement.createServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount, + publisherAccount) assert(serviceAgreement) + const status = await serviceAgreement.getStatus() assert(status === false) }) @@ -79,15 +122,20 @@ describe("ServiceAgreement", () => { describe("#grantAccess()", () => { it("should grant access in that service agreement", async () => { - const did: string = IdGenerator.generateId() + const id: string = IdGenerator.generateId() + const did: string = `did:op:${id}` + const ddo = new DDO({id: did, service: serviceDefintion}) const assetId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generateId() + // @ts-ignore + AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo)) const serviceAgreement: ServiceAgreement = - await ServiceAgreement.createServiceAgreement(testServiceAgreementTemplate, assetId, did, - consumerAccount, publisherAccount) + await ServiceAgreement.createServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount, + publisherAccount) assert(serviceAgreement) - const fulfilled: boolean = await serviceAgreement.grantAccess(did, IdGenerator.generateId()) + const fulfilled: boolean = await serviceAgreement.grantAccess(assetId, IdGenerator.generateId()) assert(fulfilled) }) }) diff --git a/test/testdata/ddo.json b/test/testdata/ddo.json index a52b407..8272643 100644 --- a/test/testdata/ddo.json +++ b/test/testdata/ddo.json @@ -1,6 +1,6 @@ { "@context": "https://w3id.org/future-method/v1", - "id": "did:op:123456789abcdefghi", + "id": "did:op:08a429b8529856d59867503f8056903a680935a76950bb9649785cc97869a43d", "publicKey": [ { "id": "did:op:123456789abcdefghi#keys-1", @@ -31,51 +31,135 @@ "publicKey": "did:op:123456789abcdefghi#keys-2" } ], + "proof": { + "type": "UUIDSignature", + "created": "2016-02-08T16:02:20Z", + "creator": "did:example:8uQhQMGzWxR8vw5P3UWH1ja", + "signatureValue": "QNB13Y7Q9...1tzjn4w==" + }, "service": [ { - "type": "OpenIdConnectVersion1.0Service", - "serviceEndpoint": "https://openid.example.com/" + "type": "Access", + "serviceDefinitionId": "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", + "conditions": [ + { + "name": "lockPayment", + "timeout": 0, + "conditionKey": { + "contractAddress": "0x...", + "fingerprint": "0x..." + }, + "parameters": { + "assetId": "bytes32", + "price": "integer" + }, + "events": { + "PaymentLocked": { + "actorType": [ + "publisher" + ], + "handlers": [ + { + "moduleName": "accessControl", + "functionName": "grantAccess", + "version": "0.1" + } + ] + } + } + }, + { + "name": "releasePayment", + "timeout": 0, + "conditionKey": { + "contractAddress": "0x...", + "fingerprint": "0xXXXXXXXX" + }, + "parameters": { + "assetId": "bytes32", + "price": "integer" + }, + "events": { + "PaymentReleased": { + "actorType": [ + "publisher" + ], + "handlers": [ + { + "moduleName": "serviceAgreement", + "functionName": "fulfillAgreement", + "version": "0.1" + } + ] + } + } + }, + { + "name": "grantAccess", + "timeout": 0, + "conditionKey": { + "contractAddress": "0x", + "fingerprint": "0xXXXXXXXX" + }, + "parameters": { + "assetId": "bytes32", + "documentKeyId": "bytes32" + }, + "events": { + "AccessGranted": { + "actorType": [ + "consumer" + ], + "handlers": [ + { + "moduleName": "asset", + "functionName": "consumeService", + "version": "0.1" + } + ] + } + } + }, + { + "name": "refundPayment", + "timeout": 1, + "condition_key": { + "contractAddress": "0x...", + "fingerprint": "0xXXXXXXXX" + }, + "parameters": { + "assetId": "bytes32", + "price": "int" + }, + "events": { + "PaymentRefund": { + "actorType": [ + "consumer" + ], + "handlers": [ + { + "moduleName": "serviceAgreement", + "functionName": "fulfillAgreement", + "version": "0.1" + } + ] + } + } + } + ] }, { - "type": "CredentialRepositoryService", - "serviceEndpoint": "https://repository.example.com/service/8377464" - }, - { - "type": "XdiService", - "serviceEndpoint": "https://xdi.example.com/8377464" - }, - { - "type": "HubService", - "serviceEndpoint": "https://hub.example.com/.identity/did:op:0123456789abcdef/" - }, - { - "type": "MessagingService", - "serviceEndpoint": "https://example.com/messages/8377464" - }, - { - "type": "SocialWebInboxService", - "serviceEndpoint": "https://social.example.com/83hfh37dj", - "description": "My public social inbox", - "spamCost": { - "amount": "0.50", - "currency": "USD" - } - }, - { - "id": "did:op:123456789abcdefghi;bops", - "type": "BopsService", - "serviceEndpoint": "https://bops.example.com/enterprise/" - }, - { - "type": "Consume", - "serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/consume?pubKey=${pubKey}&serviceId={serviceId}&url={url}" - }, - { - "type": "Compute", - "serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/compute?pubKey=${pubKey}&serviceId={serviceId}&algo={algo}&container={container}" + "type": "CloudCompute", + "serviceDefinitionId": "1", + "serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/compute?pubKey=${pubKey}&serviceId={serviceId}&algo={algo}&container={container}", + "templateId": "044852b2a670ade5407e78fb2863c51000000000000000000000000000000002" }, { "type": "Metadata", + "serviceDefinitionId": "2", "serviceEndpoint": "http://myaquarius.org/api/v1/provider/assets/metadata/{did}", "metadata": { "base": { @@ -96,13 +180,14 @@ ], "links": [ { - "sample1": "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/" + "name": "Sample of Asset Data", + "type": "sample", + "url": "https://foo.com/sample.csv" }, { - "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/" + "name": "Data Format Definition", + "type": "format", + "AssetID": "4d517500da0acb0d65a716f61330969334630363ce4a6a9d39691026ac7908ea" } ], "inLanguage": "en", @@ -130,4 +215,4 @@ } } ] -} +} \ No newline at end of file