From e4461e1be1c59edcb1de96b204a1f56d4cef5d9a Mon Sep 17 00:00:00 2001 From: ssallam Date: Mon, 1 Oct 2018 09:35:03 +0200 Subject: [PATCH 1/2] #162: new squid classes, and skeleton for the secret store. --- SQUID_INTERFACE.md | 92 +++++++++++++++++++++++ src/account.js | 37 ++++++++++ src/metadata.js | 19 +++++ src/ocean.js | 121 +++---------------------------- src/order.js | 52 +++++++++++++ src/secret_store/ParityClient.js | 22 ++++++ src/secret_store/SecretStore.js | 18 +++++ src/service.js | 36 +++++++++ src/tribe.js | 25 +++++++ 9 files changed, 312 insertions(+), 110 deletions(-) create mode 100644 SQUID_INTERFACE.md create mode 100644 src/account.js create mode 100644 src/order.js create mode 100644 src/secret_store/ParityClient.js create mode 100644 src/secret_store/SecretStore.js create mode 100644 src/service.js create mode 100644 src/tribe.js diff --git a/SQUID_INTERFACE.md b/SQUID_INTERFACE.md new file mode 100644 index 0000000..f1c7e36 --- /dev/null +++ b/SQUID_INTERFACE.md @@ -0,0 +1,92 @@ +## Ocean +- Ocean(config(web3Provider, nodeURI, gas, network, providerURI)) +- getInstance() +- getMessageHash(message) => hash of the given message +- createDIDRecord(content) => ocean specific DID with an id based on hash of the given string message +- registerProvider(url, provider_address) +- getProviders() + +## Account +- getAccounts() => list of accounts along with token and eth balances +- getTokenBalance() +- getEthBalance() +- requestTokens(amount) => bool + +## Order +- purchaseAsset(assetDID, price, timeout, conditions) +- getOrderStatus(orderId) => integer representing the order status as defined in the keeper +- getOrders() => list of orders +- verifyOrderPayment(orderId) => true / false + +## Asset / Metadata +- publishAsset(assetDID, assetDDO, price) +- updateAsset(assetDDO) +- retireAsset(assetDID) +- getAssetPrice(assetDID) +- getAssetMetadata(assetDID) => asset DDO +- getAssetsMetadata() => list of assets DDOs +- resolveAssetDID(did) => DDO of the given DID +- getAssets() => asset ids from keeper +- checkAsset(assetDID) => true / false +- getAssetConditions + + +####################################################### +####################################################### +####################################################### +## Keeper API: +* register tribe actor +* publish service agreement conditions +* setup/execute service agreement +* fulfill condition +* dispute service delivery, quality, content, results +* submit proof of service delivery +* query transaction history +* verify actor + +####################################################### +## Use cases: + +### Publisher +* publish one service + * squid.publishService(metadata) => serviceDID, didDocument +* view my published services + * squid.listServices(type, matchStr, labels, tags, publishers)) +* view my sold services + * squid.listSoldServices(actorAddress) +* update service + * squid.updateService(serviceDID, metadata, price) +* revoke service + * squid.revokeService(serviceDID, reason) + +### Consumer +* view / find services + * squid.listServices(type, matchStr, labels, tags, publishers) +* buy/access service + * squid.buyService(serviceId, ) => orderId, service ? +* view my purchased services + * squid.listPurchasedServices(actorAddress) +* checkPurchaseStatus(orderId) +* dispute purchased service + * squid.openServiceDispute(orderId) +* rate service + * squid.rateService(orderId, rating) + +### Tribe / Marketplace +* register on ocean with an `ethereum` address and DID Record + * Uses `squid` or directly access keeper contracts +* provide API to allow the following interactions: + * publish a service + * update a service + * revoke a service + * buy/use/access a service + * search available services + * +* tribe admin: + * view publishers + * view disputes + +### Curator + +### Verifier + diff --git a/src/account.js b/src/account.js new file mode 100644 index 0000000..cb84b7f --- /dev/null +++ b/src/account.js @@ -0,0 +1,37 @@ +import KeeperBase from './keeper/keeper-base' + +export default class Account extends KeeperBase { + constructor(web3Helper, token) { + super(web3Helper) + this.token = token + return (async () => { + return this + })() + } + + async list() { + return Promise.all((await this.helper.getAccounts()).map(async (account) => { + // await ocean.market.requestTokens(account, 1000) + + return { + name: account, + balance: { + ocn: await this.token.getTokenBalance(account), + eth: await this.token.getEthBalance(account) + } + } + })) + } + + currentAccount() { + return this.helper.getCurrentAccount() + } + + tokenBalance() { + return this.token.getTokenBalance() + } + + ethBalance() { + return this.token.getEthBalance() + } +} diff --git a/src/metadata.js b/src/metadata.js index 4889486..c620b7d 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -40,4 +40,23 @@ export default class MetaData { return false }) } + async publishDataAsset(assetMetadata, price) { + // Register on-chain (in the keeper) + const { market } = this.contracts + const assetDID = await this.generateDID(assetMetadata) + const result = await market.register( + assetDID, + price, + { from: this.getCurrentAccount(), gas: this.defaultGas } + ) + if (!result) { + throw Error('Register asset in ocean keeper failed.') + } + // Register in oceandb + const assetDDO = this.createAssetDDO(assetDID, assetMetadata) + this.metadata.publishDataAsset(assetDID, assetDDO) + return assetDDO + } + + } diff --git a/src/ocean.js b/src/ocean.js index 81236a9..7c1c0b0 100644 --- a/src/ocean.js +++ b/src/ocean.js @@ -1,11 +1,12 @@ import Web3 from 'web3' +import OceanMarket from './keeper/market' +import OceanAuth from './keeper/auth' +import OceanToken from './keeper/token' import Logger from './utils/logger' import Web3Helper from './utils/Web3Helper' import MetaData from './metadata' -import ContractLoader from './keeper/contractLoader' const DEFAULT_GAS = 300000 -const contractsToLoad = { market: 'OceanMarket', token: 'OceanToken', auth: 'OceanAuth' } export default class Ocean { constructor(config) { @@ -16,33 +17,20 @@ export default class Ocean { this.helper = new Web3Helper(this._web3) this.metadata = new MetaData(this._providerUri) - this.contracts = {} + return (async () => { this._network = config.network || (await this.helper.getNetworkName()).toLowerCase() || 'development' - for (const key of contractsToLoad) { - this.contracts[key] = await ContractLoader.load(contractsToLoad[key], this.helper) - } + + this.market = await new OceanMarket(this.helper) + this.auth = await new OceanAuth(this.helper) + this.token = await new OceanToken(this.helper) return this })() } - async getAccounts() { - return Promise.all((await this.helper.getAccounts()).map(async (account) => { - // await ocean.market.requestTokens(account, 1000) - - return { - name: account, - balance: { - ocn: await this.contracts.token.getTokenBalance(account), - eth: await this.contracts.token.getEthBalance(account) - } - } - })) - } - async getOrdersByConsumer(consumerAddress) { - let accessConsentEvent = this.contracts.auth.AccessConsentRequested({ _consumer: consumerAddress }, { + let accessConsentEvent = this.auth.contract.AccessConsentRequested({ _consumer: consumerAddress }, { fromBlock: 0, toBlock: 'latest' }) @@ -80,47 +68,6 @@ export default class Ocean { return orders } - purchaseAsset( - assetId, publisherId, price, privateKey, publicKey, timeout, senderAddress, - initialRequestEventHandler, accessCommittedEventHandler, tokenPublishedEventHandler) { - const { token, market, auth } = this.contracts - // Allow market contract to transfer funds on the consumer's behalf - token.approve(market.address, price, { from: senderAddress, gas: 2000000 }) - // Submit the access request - auth.initiateAccessRequest( - assetId, publisherId, publicKey, - timeout, { from: senderAddress, gas: 1000000 } - ) - - const resourceFilter = { _resourceId: assetId, _consumer: senderAddress } - const initRequestEvent = auth.AccessConsentRequested(resourceFilter) - let order = {} - this._listenOnce( - initRequestEvent, - 'AccessConsentRequested', - (result, error) => { - order = initialRequestEventHandler(result, error) - const requestIdFilter = { _id: order.id } - const accessCommittedEvent = auth.AccessRequestCommitted(requestIdFilter) - const tokenPublishedEvent = auth.EncryptedTokenPublished(requestIdFilter) - this._listenOnce( - accessCommittedEvent, - 'AccessRequestCommitted', - (result, error) => { - accessCommittedEventHandler(result, order, error) - } - ) - this._listenOnce( - tokenPublishedEvent, - 'EncryptedTokenPublished', - (result, error) => { - tokenPublishedEventHandler(result, order, error) - } - ) - }) - return order - } - // Helper functions (private) _listenOnce(event, eventName, callback) { // eslint-disable-next-line security/detect-non-literal-fs-filename @@ -134,38 +81,9 @@ export default class Ocean { } // The new interface - async publishDataAsset(assetMetadata, price) { - // Register on-chain (in the keeper) - const { market } = this.contracts - const assetDID = await this.generateDID(assetMetadata) - const result = await market.register( - assetDID, - price, - { from: this.getCurrentAccount(), gas: this.defaultGas } - ) - if (!result) { - throw Error('Register asset in ocean keeper failed.') - } - // Register in oceandb - const assetDDO = this.createAssetDDO(assetDID, assetMetadata) - this.metadata.publishDataAsset(assetDID, assetDDO) - return assetDDO - } - - getCurrentAccount() { - return this.helper.getCurrentAccount() - } - - getTokenBalance() { - return this.contracts.token.getTokenBalance() - } - - getEthBalance() { - return this.contracts.token.getEthBalance() - } requestTokens(numTokens) { - return this.contracts.market.requestTokens(numTokens, { from: this.getCurrentAccount() }) + return this.market.requestTokens(numTokens, { from: this.getCurrentAccount() }) } getMessageHash(message) { @@ -173,23 +91,6 @@ export default class Ocean { } async generateDID(content) { - return 'did:ocn:' + (await this.contracts.market.generateId(content)).toString() - } - - createAssetDDO(assetDID, assetMetadata) { - return { - '@context': 'https://w3id.org/did/v1', - id: assetDID, - publicKey: [], - authentication: [], - service: [], - metadata: assetMetadata - } - } - - resolveDID(did) { - const providerURL = this.contracts.market.resolveAssetDID(did) - const metadataGuy = new MetaData(providerURL) - return metadataGuy.getAssetDDO(did) + return 'did:ocn:' + (await this.market.generateId(content)).toString() } } diff --git a/src/order.js b/src/order.js new file mode 100644 index 0000000..2e35693 --- /dev/null +++ b/src/order.js @@ -0,0 +1,52 @@ +import KeeperBase from './keeper/keeper-base' + +export default class Order extends KeeperBase { + constructor(web3Helper, market, token, auth) { + super(web3Helper) + this.market = market + this.token = token + this.auth = auth + return (async () => { + return this + })() + } + + buyService(serviceDID, publisherId, price, timeout, senderAddress, initialRequestEventHandler, + accessCommittedEventHandler, tokenPublishedEventHandler) { + // Allow market contract to transfer funds on the consumer's behalf + const { token, market, auth } = this + token.contract.approve(market.address, price, { from: senderAddress, gas: 2000000 }) + // Submit the access request + auth.contract.initiateAccessRequest( + serviceDID, publisherId, timeout, { from: senderAddress, gas: 1000000 } + ) + + const resourceFilter = { _resourceId: serviceDID, _consumer: senderAddress } + const initRequestEvent = auth.contract.AccessConsentRequested(resourceFilter) + let order = {} + this._listenOnce( + initRequestEvent, + 'AccessConsentRequested', + (result, error) => { + order = initialRequestEventHandler(result, error) + const requestIdFilter = { _id: order.id } + const accessCommittedEvent = auth.contract.AccessRequestCommitted(requestIdFilter) + const tokenPublishedEvent = auth.contract.EncryptedTokenPublished(requestIdFilter) + this._listenOnce( + accessCommittedEvent, + 'AccessRequestCommitted', + (result, error) => { + accessCommittedEventHandler(result, order, error) + } + ) + this._listenOnce( + tokenPublishedEvent, + 'EncryptedTokenPublished', + (result, error) => { + tokenPublishedEventHandler(result, order, error) + } + ) + }) + return order + } +} diff --git a/src/secret_store/ParityClient.js b/src/secret_store/ParityClient.js new file mode 100644 index 0000000..39f559e --- /dev/null +++ b/src/secret_store/ParityClient.js @@ -0,0 +1,22 @@ +export default class ParityClient { + constructor(url) { + this.url = url + this.web3 = null + } + + signDocumentKeyId() { + return '' + } + + generateDocumentKeyFromKey() { + return '' + } + + encryptDocument() { + return '' + } + + decryptDocument() { + return '' + } +} \ No newline at end of file diff --git a/src/secret_store/SecretStore.js b/src/secret_store/SecretStore.js new file mode 100644 index 0000000..2aa606e --- /dev/null +++ b/src/secret_store/SecretStore.js @@ -0,0 +1,18 @@ +export default class SecretStore { + constructor(url, threshold) { + this.url = url + this.threshold = threshold | 1 + } + + generateServerKey() { + return '' + } + + storeDocumentKey() { + return '' + } + + retrieveDocumentKey() { + return '' + } +} diff --git a/src/service.js b/src/service.js new file mode 100644 index 0000000..f1a05b4 --- /dev/null +++ b/src/service.js @@ -0,0 +1,36 @@ +import KeeperBase from './keeper/keeper-base' +import MetaData from './metadata' + +export default class Service extends KeeperBase { + constructor(web3Helper, market) { + super(web3Helper) + this.market = market + return (async () => { + return this + })() + } + + serviceMetadata(assetDID) { + return '' + } + serviceTribe() { + return '' + } + + createDDO(did, metadata) { + return { + '@context': 'https://w3id.org/did/v1', + id: did, + publicKey: [], + authentication: [], + service: [], + metadata: metadata + } + } + + resolveDID(did) { + const providerURL = this.market.resolveDID(did) + const metadataGuy = new MetaData(providerURL) + return metadataGuy.getAssetDDO(did) + } +} diff --git a/src/tribe.js b/src/tribe.js new file mode 100644 index 0000000..995609b --- /dev/null +++ b/src/tribe.js @@ -0,0 +1,25 @@ +import KeeperBase from './keeper/keeper-base' + +export default class Tribe extends KeeperBase { + constructor(web3Helper) { + super(web3Helper) + + return (async () => { + return this + })() + } + + // did ddo for tribes/marketplaces + registerTribe() { + return '' + } + + tribessList() { + return '' + } + + resolveTribeDID() { + // verify DDO + return 'DDO' + } +} From 8dd807599a4ed22b638e9ef30d702035fb7af324 Mon Sep 17 00:00:00 2001 From: ssallam Date: Mon, 1 Oct 2018 10:19:12 +0200 Subject: [PATCH 2/2] #162: remove secret store stuff, moved to own repo. --- src/secret_store/ParityClient.js | 22 ---------------------- src/secret_store/SecretStore.js | 18 ------------------ 2 files changed, 40 deletions(-) delete mode 100644 src/secret_store/ParityClient.js delete mode 100644 src/secret_store/SecretStore.js diff --git a/src/secret_store/ParityClient.js b/src/secret_store/ParityClient.js deleted file mode 100644 index 39f559e..0000000 --- a/src/secret_store/ParityClient.js +++ /dev/null @@ -1,22 +0,0 @@ -export default class ParityClient { - constructor(url) { - this.url = url - this.web3 = null - } - - signDocumentKeyId() { - return '' - } - - generateDocumentKeyFromKey() { - return '' - } - - encryptDocument() { - return '' - } - - decryptDocument() { - return '' - } -} \ No newline at end of file diff --git a/src/secret_store/SecretStore.js b/src/secret_store/SecretStore.js deleted file mode 100644 index 2aa606e..0000000 --- a/src/secret_store/SecretStore.js +++ /dev/null @@ -1,18 +0,0 @@ -export default class SecretStore { - constructor(url, threshold) { - this.url = url - this.threshold = threshold | 1 - } - - generateServerKey() { - return '' - } - - storeDocumentKey() { - return '' - } - - retrieveDocumentKey() { - return '' - } -}