diff --git a/src/ocean/Ocean.ts b/src/ocean/Ocean.ts index 5045f1e..c4eda04 100644 --- a/src/ocean/Ocean.ts +++ b/src/ocean/Ocean.ts @@ -1,31 +1,23 @@ import deprecated from "deprecated-decorator" +import OceanAccounts from "./OceanAccounts" +import OceanAgreements from "./OceanAgreements" +import OceanAssets from "./OceanAssets" + 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 Condition from "../ddo/Condition" -import Contract from "../ddo/Contract" import DDO from "../ddo/DDO" -import Event from "../ddo/Event" -import EventHandler from "../ddo/EventHandler" import MetaData from "../ddo/MetaData" -import PublicKey from "../ddo/PublicKey" import Service from "../ddo/Service" import ContractEvent from "../keeper/Event" -import Keeper from "../keeper/Keeper" -import Web3Provider from "../keeper/Web3Provider" import Config from "../models/Config" -import ValueType from "../models/ValueType" import SecretStoreProvider from "../secretstore/SecretStoreProvider" import Logger from "../utils/Logger" import Account from "./Account" import DID from "./DID" -import IdGenerator from "./IdGenerator" import ServiceAgreement from "./ServiceAgreements/ServiceAgreement" -import ServiceAgreementTemplate from "./ServiceAgreements/ServiceAgreementTemplate" -import Access from "./ServiceAgreements/Templates/Access" import EventListener from "../keeper/EventListener" @@ -43,7 +35,9 @@ export default class Ocean { if (!Ocean.instance) { ConfigProvider.setConfig(config) Ocean.instance = new Ocean() - Ocean.instance.keeper = await Keeper.getInstance() + Ocean.instance.accounts = await OceanAccounts.getInstance() + Ocean.instance.assets = await OceanAssets.getInstance() + Ocean.instance.agreements = await OceanAgreements.getInstance() } return Ocean.instance @@ -56,215 +50,103 @@ export default class Ocean { private static instance: Ocean = null /** - * Keeper instance. - * @type {Keeper} + * Ocean account submodule + * @type {OceanAccounts} */ - private keeper: Keeper + public accounts: OceanAccounts + + /** + * Ocean assets submodule + * @type {OceanAssets} + */ + public assets: OceanAssets + + /** + * Ocean agreements submodule + * @type {OceanAgreements} + */ + public agreements: OceanAgreements private constructor() { } /** * Returns the list of accounts. + * @deprecated Replace by [Ocean.accounts.list]{@link #OceanAccounts.list} * @return {Promise} */ + @deprecated("OceanAccounts.list") public async getAccounts(): Promise { - - // retrieve eth accounts - const ethAccounts = await Web3Provider.getWeb3().eth.getAccounts() - - return ethAccounts.map((address: string) => new Account(address)) + return await this.accounts.list() } /** * Returns a DDO by DID. - * @deprecated Replace by {@link #resolveAssetDID} + * @deprecated Replace by [Ocean.assets.resolve]{@link #OceanAssets.resolve} * @param {string} did Decentralized ID. * @return {Promise} */ - @deprecated("resolveAssetDID") + @deprecated("OceanAssets.resolve") public async resolveDID(did: string): Promise { - return await this.resolveAssetDID(did) + return await this.assets.resolve(did) } /** * Returns a DDO by DID. + * @deprecated Replace by [Ocean.assets.resolve]{@link #OceanAssets.resolve} * @param {string} did Decentralized ID. * @return {Promise} */ + @deprecated("OceanAssets.resolve") public async resolveAssetDID(did: string): Promise { - const d: DID = DID.parse(did) - return AquariusProvider.getAquarius().retrieveDDO(d) + return await this.assets.resolve(did) } /** * Registers a new DDO. + * @deprecated Replace by [Ocean.assets.create]{@link #OceanAssets.create} * @param {MetaData} metadata DDO metadata. * @param {Account} publisher Publicher account. * @return {Promise} */ + @deprecated("OceanAssets.create") public async registerAsset(metadata: MetaData, publisher: Account): Promise { - const {didRegistry} = this.keeper - const aquarius = AquariusProvider.getAquarius() - const brizo = BrizoProvider.getBrizo() - - const did: DID = DID.generate() - const accessServiceDefinitionId: string = "0" - const computeServiceDefintionId: string = "1" - const metadataServiceDefinitionId: string = "2" - - metadata.base.contentUrls = - [await SecretStoreProvider.getSecretStore() - .encryptDocument(did.getId(), metadata.base.contentUrls)] - - const template = new Access() - const serviceAgreementTemplate = new ServiceAgreementTemplate(template) - - const conditions: Condition[] = await serviceAgreementTemplate.getConditions(metadata, did.getId()) - - const serviceEndpoint = aquarius.getServiceEndpoint(did) - - // create ddo itself - const ddo: DDO = new DDO({ - authentication: [{ - type: "RsaSignatureAuthentication2018", - publicKey: did.getDid() + "#keys-1", - } as Authentication], - id: did.getDid(), - publicKey: [ - { - id: did.getDid() + "#keys-1", - type: "Ed25519VerificationKey2018", - owner: did.getDid(), - publicKeyBase58: await publisher.getPublicKey(), - } as PublicKey, - ], - service: [ - { - type: template.templateName, - purchaseEndpoint: brizo.getPurchaseEndpoint(), - serviceEndpoint: brizo.getConsumeEndpoint(), - // the id of the service agreement? - serviceDefinitionId: accessServiceDefinitionId, - // the id of the service agreement template - templateId: serviceAgreementTemplate.getId(), - serviceAgreementContract: { - contractName: "ServiceAgreement", - fulfillmentOperator: template.fulfillmentOperator, - events: [ - { - name: "ExecuteAgreement", - actorType: "consumer", - handler: { - moduleName: "payment", - functionName: "lockPayment", - version: "0.1", - } as EventHandler, - } as Event, - ], - } as Contract, - conditions, - } as Service, - { - type: "Compute", - serviceEndpoint: brizo.getComputeEndpoint(publisher.getId(), - computeServiceDefintionId, "xxx", "xxx"), - serviceDefinitionId: computeServiceDefintionId, - } as Service, - { - type: "Metadata", - serviceEndpoint, - serviceDefinitionId: metadataServiceDefinitionId, - metadata, - } as Service, - ], - }) - - const storedDdo = await aquarius.storeDDO(ddo) - - // Logger.log(JSON.stringify(storedDdo, null, 2)) - - await didRegistry.registerAttribute( - did.getId(), - ValueType.URL, - "Metadata", - serviceEndpoint, - publisher.getId()) - - return storedDdo + return await this.assets.create(metadata, publisher) } /** * Signs a service agreement by DID. - * @deprecated Replace by {@link #purchaseAssetService} + * @deprecated Replace by [Ocean.assets.order]{@link #OceanAssets.order} * @param {string} did Decentralized ID. * @param {string} serviceDefinitionId Service definition ID. * @param {Account} consumer Consumer account. * @return {Promise} * */ - @deprecated("purchaseAssetService") + @deprecated("OceanAssets.order") public async signServiceAgreement( did: string, serviceDefinitionId: string, consumer: Account, ): Promise { - return await this.purchaseAssetService(did, serviceDefinitionId, consumer) + return await this.assets.order(did, serviceDefinitionId, consumer) } /** * Signs a service agreement by DID. + * @deprecated Replace by [Ocean.assets.order]{@link #OceanAssets.order} * @param {string} did Decentralized ID. * @param {string} serviceDefinitionId Service definition ID. * @param {Account} consumer Consumer account. * @return {Promise} */ + @deprecated("OceanAssets.order") public async purchaseAssetService( did: string, serviceDefinitionId: string, consumer: Account, ): Promise { - - const d: DID = DID.parse(did as string) - const ddo = await AquariusProvider.getAquarius().retrieveDDO(d) - const serviceAgreementId: string = IdGenerator.generateId() - - try { - const serviceAgreementSignature: string = await ServiceAgreement.signServiceAgreement( - ddo, serviceDefinitionId, serviceAgreementId, consumer) - - const accessService: Service = ddo.findServiceByType("Access") - const metadataService: Service = ddo.findServiceByType("Metadata") - - const price = metadataService.metadata.base.price - const balance = await consumer.getOceanBalance() - if (balance < price) { - throw new Error(`Not enough ocean tokens! Should have ${price} but has ${balance}`) - } - - const event: ContractEvent = EventListener.subscribe( - accessService.serviceAgreementContract.contractName, - accessService.serviceAgreementContract.events[0].name, { - serviceAgreementId: `0x${serviceAgreementId}`, - }) - - event.listenOnce(async (data) => { - const sa: ServiceAgreement = new ServiceAgreement(data.returnValues.serviceAgreementId) - await sa.payAsset( - d.getId(), - metadataService.metadata.base.price, - consumer, - ) - Logger.log("Completed asset payment, now access should be granted.") - }) - - return { - serviceAgreementId, - serviceAgreementSignature, - } - } catch (err) { - Logger.error("Signing ServiceAgreement failed!", err) - } + return await this.assets.order(did, serviceDefinitionId, consumer) } /** @@ -330,6 +212,7 @@ export default class Ocean { /** * Executes a service agreement. + * @deprecated Replace by [Ocean.agreements.create]{@link #OceanAgreements.create} * @param {string} did Decentralized ID. * @param {string} serviceDefinitionId Service definition ID. * @param {string} serviceAgreementId Service agreement ID. @@ -338,6 +221,7 @@ export default class Ocean { * @param {Account} publisher Publisher account. * @return {Promise} */ + @deprecated("OceanAgreements.create") public async executeServiceAgreement( did: string, serviceDefinitionId: string, @@ -346,47 +230,35 @@ export default class Ocean { consumer: Account, publisher: Account, ): Promise { - const d: DID = DID.parse(did) - const ddo = await AquariusProvider.getAquarius().retrieveDDO(d) - - const serviceAgreement: ServiceAgreement = await ServiceAgreement - .executeServiceAgreement( - d, - ddo, + return await this.agreements + .create(did, serviceDefinitionId, serviceAgreementId, serviceAgreementSignature, consumer, - publisher) - - return serviceAgreement + publisher, + ) } /** * Search over the assets using a query. + * @deprecated Replace by [Ocean.assets.query]{@link #OceanAssets.query} * @param {SearchQuery} query Query to filter the assets. * @return {Promise} */ + @deprecated("OceanAssets.query") public async searchAssets(query: SearchQuery): Promise { - return AquariusProvider.getAquarius().queryMetadata(query) + return await this.assets.query(query) } /** * Search over the assets using a keyword. - * @param {SearchQuery} text Text to filter the assets. + * @deprecated Replace by [Ocean.assets.search]{@link #OceanAssets.search} + * @param {string} text Text to filter the assets. * @return {Promise} */ + @deprecated("OceanAssets.search") public async searchAssetsByText(text: string): Promise { - return AquariusProvider.getAquarius().queryMetadataByText({ - text, - page: 0, - offset: 100, - query: { - value: 1, - }, - sort: { - value: 1, - }, - } as SearchQuery) + return await this.assets.search(text) } } diff --git a/src/ocean/OceanAccounts.ts b/src/ocean/OceanAccounts.ts new file mode 100644 index 0000000..3a5e025 --- /dev/null +++ b/src/ocean/OceanAccounts.ts @@ -0,0 +1,39 @@ +import Web3Provider from "../keeper/Web3Provider" + +import Account from "./Account" + +/** + * Account submodule of Ocean Protocol. + */ +export default class OceanAccounts { + + /** + * Returns the instance of OceanAccounts. + * @return {Promise} + */ + public static async getInstance(): Promise { + if (!OceanAccounts.instance) { + OceanAccounts.instance = new OceanAccounts() + } + + return OceanAccounts.instance + } + + /** + * OceanAccounts instance. + * @type {OceanAccounts} + */ + private static instance: OceanAccounts = null + + /** + * Returns the list of accounts. + * @return {Promise} + */ + public async list(): Promise { + + // retrieve eth accounts + const ethAccounts = await Web3Provider.getWeb3().eth.getAccounts() + + return ethAccounts.map((address: string) => new Account(address)) + } +} diff --git a/src/ocean/OceanAgreements.ts b/src/ocean/OceanAgreements.ts new file mode 100644 index 0000000..6f87344 --- /dev/null +++ b/src/ocean/OceanAgreements.ts @@ -0,0 +1,62 @@ +import AquariusProvider from "../aquarius/AquariusProvider" +import Account from "./Account" +import DID from "./DID" +import ServiceAgreement from "./ServiceAgreements/ServiceAgreement" + +/** + * Agreements submodule of Ocean Protocol. + */ +export default class OceanAgreements { + + /** + * Returns the instance of OceanAgreements. + * @return {Promise} + */ + public static async getInstance(): Promise { + if (!OceanAgreements.instance) { + OceanAgreements.instance = new OceanAgreements() + } + + return OceanAgreements.instance + } + + /** + * OceanAgreements instance. + * @type {OceanAgreements} + */ + private static instance: OceanAgreements = null + + /** + * Executes a service agreement. + * @param {string} did Decentralized ID. + * @param {string} serviceDefinitionId Service definition ID. + * @param {string} serviceAgreementId Service agreement ID. + * @param {string} serviceAgreementSignature Service agreement signature. + * @param {Account} consumer Consumer account. + * @param {Account} publisher Publisher account. + * @return {Promise} + */ + public async create( + did: string, + serviceDefinitionId: string, + serviceAgreementId: string, + serviceAgreementSignature: string, + consumer: Account, + publisher: Account, + ): Promise { + const d: DID = DID.parse(did) + const ddo = await AquariusProvider.getAquarius().retrieveDDO(d) + + const serviceAgreement: ServiceAgreement = await ServiceAgreement + .executeServiceAgreement( + d, + ddo, + serviceDefinitionId, + serviceAgreementId, + serviceAgreementSignature, + consumer, + publisher) + + return serviceAgreement + } +} diff --git a/src/ocean/OceanAssets.ts b/src/ocean/OceanAssets.ts new file mode 100644 index 0000000..906e445 --- /dev/null +++ b/src/ocean/OceanAssets.ts @@ -0,0 +1,240 @@ +import AquariusProvider from "../aquarius/AquariusProvider" +import SearchQuery from "../aquarius/query/SearchQuery" +import BrizoProvider from "../brizo/BrizoProvider" +import Authentication from "../ddo/Authentication" +import Condition from "../ddo/Condition" +import Contract from "../ddo/Contract" +import DDO from "../ddo/DDO" +import Event from "../ddo/Event" +import EventHandler from "../ddo/EventHandler" +import MetaData from "../ddo/MetaData" +import PublicKey from "../ddo/PublicKey" +import Service from "../ddo/Service" +import ContractEvent from "../keeper/Event" +import EventListener from "../keeper/EventListener" +import Keeper from "../keeper/Keeper" +import ValueType from "../models/ValueType" +import SecretStoreProvider from "../secretstore/SecretStoreProvider" +import Logger from "../utils/Logger" +import Account from "./Account" +import DID from "./DID" +import IdGenerator from "./IdGenerator" +import ServiceAgreement from "./ServiceAgreements/ServiceAgreement" +import ServiceAgreementTemplate from "./ServiceAgreements/ServiceAgreementTemplate" +import Access from "./ServiceAgreements/Templates/Access" + +/** + * Assets submodule of Ocean Protocol. + */ +export default class OceanAssets { + + /** + * Returns the instance of OceanAssets. + * @return {Promise} + */ + public static async getInstance(): Promise { + if (!OceanAssets.instance) { + OceanAssets.instance = new OceanAssets() + } + + return OceanAssets.instance + } + + /** + * OceanAssets instance. + * @type {OceanAssets} + */ + private static instance: OceanAssets = null + + /** + * Returns a DDO by DID. + * @param {string} did Decentralized ID. + * @return {Promise} + */ + public async resolve(did: string): Promise { + const d: DID = DID.parse(did) + return AquariusProvider.getAquarius().retrieveDDO(d) + } + + /** + * Creates a new DDO. + * @param {MetaData} metadata DDO metadata. + * @param {Account} publisher Publicher account. + * @return {Promise} + */ + public async create(metadata: MetaData, publisher: Account): Promise { + const {didRegistry} = await Keeper.getInstance() + const aquarius = AquariusProvider.getAquarius() + const brizo = BrizoProvider.getBrizo() + + const did: DID = DID.generate() + const accessServiceDefinitionId: string = "0" + const computeServiceDefintionId: string = "1" + const metadataServiceDefinitionId: string = "2" + + metadata.base.contentUrls = + [await SecretStoreProvider.getSecretStore() + .encryptDocument(did.getId(), metadata.base.contentUrls)] + + const template = new Access() + const serviceAgreementTemplate = new ServiceAgreementTemplate(template) + + const conditions: Condition[] = await serviceAgreementTemplate.getConditions(metadata, did.getId()) + + const serviceEndpoint = aquarius.getServiceEndpoint(did) + + // create ddo itself + const ddo: DDO = new DDO({ + authentication: [{ + type: "RsaSignatureAuthentication2018", + publicKey: did.getDid() + "#keys-1", + } as Authentication], + id: did.getDid(), + publicKey: [ + { + id: did.getDid() + "#keys-1", + type: "Ed25519VerificationKey2018", + owner: did.getDid(), + publicKeyBase58: await publisher.getPublicKey(), + } as PublicKey, + ], + service: [ + { + type: template.templateName, + purchaseEndpoint: brizo.getPurchaseEndpoint(), + serviceEndpoint: brizo.getConsumeEndpoint(), + // the id of the service agreement? + serviceDefinitionId: accessServiceDefinitionId, + // the id of the service agreement template + templateId: serviceAgreementTemplate.getId(), + serviceAgreementContract: { + contractName: "ServiceAgreement", + fulfillmentOperator: template.fulfillmentOperator, + events: [ + { + name: "ExecuteAgreement", + actorType: "consumer", + handler: { + moduleName: "payment", + functionName: "lockPayment", + version: "0.1", + } as EventHandler, + } as Event, + ], + } as Contract, + conditions, + } as Service, + { + type: "Compute", + serviceEndpoint: brizo.getComputeEndpoint(publisher.getId(), + computeServiceDefintionId, "xxx", "xxx"), + serviceDefinitionId: computeServiceDefintionId, + } as Service, + { + type: "Metadata", + serviceEndpoint, + serviceDefinitionId: metadataServiceDefinitionId, + metadata, + } as Service, + ], + }) + + const storedDdo = await aquarius.storeDDO(ddo) + + // Logger.log(JSON.stringify(storedDdo, null, 2)) + + await didRegistry.registerAttribute( + did.getId(), + ValueType.URL, + "Metadata", + serviceEndpoint, + publisher.getId()) + + return storedDdo + } + + /** + * Purchases a service agreement by DID. + * @param {string} did Decentralized ID. + * @param {string} serviceDefinitionId Service definition ID. + * @param {Account} consumer Consumer account. + * @return {Promise<{serviceAgreementId: string, serviceAgreementSignature: string}>} + */ + public async order( + did: string, + serviceDefinitionId: string, + consumer: Account, + ): Promise<{serviceAgreementId: string, serviceAgreementSignature: string}> { + + const d: DID = DID.parse(did as string) + const ddo = await AquariusProvider.getAquarius().retrieveDDO(d) + const serviceAgreementId: string = IdGenerator.generateId() + + try { + const serviceAgreementSignature: string = await ServiceAgreement.signServiceAgreement( + ddo, serviceDefinitionId, serviceAgreementId, consumer) + + const accessService: Service = ddo.findServiceByType("Access") + const metadataService: Service = ddo.findServiceByType("Metadata") + + const price = metadataService.metadata.base.price + const balance = await consumer.getOceanBalance() + if (balance < price) { + throw new Error(`Not enough ocean tokens! Should have ${price} but has ${balance}`) + } + + const event: ContractEvent = EventListener.subscribe( + accessService.serviceAgreementContract.contractName, + accessService.serviceAgreementContract.events[0].name, { + serviceAgreementId, + }) + + event.listenOnce(async (data) => { + + const sa: ServiceAgreement = new ServiceAgreement(data.returnValues.serviceAgreementId) + await sa.payAsset( + d.getId(), + metadataService.metadata.base.price, + consumer, + ) + Logger.log("Completed asset payment, now access should be granted.") + }) + + return { + serviceAgreementId, + serviceAgreementSignature, + } + + } catch (err) { + Logger.error("Signing ServiceAgreement failed!", err) + } + } + + /** + * Search over the assets using a query. + * @param {SearchQuery} query Query to filter the assets. + * @return {Promise} + */ + public async query(query: SearchQuery): Promise { + return AquariusProvider.getAquarius().queryMetadata(query) + } + + /** + * Search over the assets using a keyword. + * @param {SearchQuery} text Text to filter the assets. + * @return {Promise} + */ + public async search(text: string): Promise { + return AquariusProvider.getAquarius().queryMetadataByText({ + text, + page: 0, + offset: 100, + query: { + value: 1, + }, + sort: { + value: 1, + }, + } as SearchQuery) + } +} diff --git a/test/ocean/Ocean.test.ts b/test/ocean/Ocean.test.ts index f95898b..986793c 100644 --- a/test/ocean/Ocean.test.ts +++ b/test/ocean/Ocean.test.ts @@ -151,7 +151,7 @@ describe("Ocean", () => { describe("#executeServiceAgreement()", () => { - it("should execute an service agreement", async () => { + it("should execute a service agreement", async () => { const publisher = accounts[0] const consumer = accounts[1]