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

added purchase functiuonality

This commit is contained in:
Sebastian Gerske 2018-10-17 18:24:01 +02:00
parent 9fb6f296f3
commit 5719a667ea
20 changed files with 312 additions and 229 deletions

5
package-lock.json generated
View File

@ -4181,6 +4181,11 @@
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
}, },
"node-fetch": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz",
"integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA=="
},
"node-libs-browser": { "node-libs-browser": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz",

View File

@ -55,6 +55,7 @@
"eth-ecies": "^1.0.3", "eth-ecies": "^1.0.3",
"ethereumjs-util": "^5.2.0", "ethereumjs-util": "^5.2.0",
"jsonwebtoken": "^8.3.0", "jsonwebtoken": "^8.3.0",
"node-fetch": "^2.2.0",
"web3": "1.0.0-beta.36", "web3": "1.0.0-beta.36",
"web3-utils": "1.0.0-beta.36" "web3-utils": "1.0.0-beta.36"
}, },

View File

@ -1,5 +1,7 @@
import {Receipt} from "web3-utils" import {Receipt} from "web3-utils"
import AccessStatus from "../models/AccessStatus"
import Asset from "../ocean/Asset" import Asset from "../ocean/Asset"
import Order from "../ocean/Order"
import ContractBaseWrapper from "./ContractWrapperBase" import ContractBaseWrapper from "./ContractWrapperBase"
export default class OceanAuth extends ContractBaseWrapper { export default class OceanAuth extends ContractBaseWrapper {
@ -10,34 +12,36 @@ export default class OceanAuth extends ContractBaseWrapper {
return auth return auth
} }
public async getOrderStatus(orderId: string): Promise<number> { public async getOrderStatus(orderId: string): Promise<AccessStatus> {
return this.contract.methods.statusOfAccessRequest(orderId) return this.call("statusOfAccessRequest", [orderId])
.call() .then((status: string) => {
.then((status: string) => parseInt(status, 10)) const statusInt = parseInt(status, 10)
} const statusString = AccessStatus[statusInt]
return AccessStatus[statusString]
public async cancelAccessRequest(orderId: string, senderAddress: string): Promise<Receipt> {
return this.contract.methods.cancelAccessRequest(orderId)
.send({
from: senderAddress,
}) })
} }
public async getEncryptedAccessToken(orderId: string, senderAddress: string): Promise<Receipt> { public async getEncryptedAccessToken(orderId: string, consumerAddress: string): Promise<Receipt> {
return this.contract.methods.getEncryptedAccessToken(orderId) return this.call("getEncryptedAccessToken", [orderId], consumerAddress)
.send({
from: senderAddress,
})
} }
public async initiateAccessRequest(asset: Asset, publicKey: string, public async initiateAccessRequest(asset: Asset, publicKey: string,
timeout: number, buyerAddress: string): Promise<Receipt> { timeout: number, buyerAddress: string): Promise<Receipt> {
const args = [asset.getId(), asset.publisher.getId(), publicKey, timeout] const args = [asset.getId(), asset.publisher.getId(), publicKey, timeout]
return this.sendTransaction("initiateAccessRequest", buyerAddress, args) return this.sendTransaction("initiateAccessRequest", buyerAddress, args)
} }
public async commitAccessRequest() { public async commitAccessRequest(order: Order, publisherAddress: string) {
// todo const args = [order.getId(), true, 9999999999, "discovery", "read", "slaLink", "slaType"]
return this.sendTransaction("commitAccessRequest", publisherAddress, args)
} }
public async getTempPubKey(orderId: string) {
return this.call("getTempPubKey", [orderId])
}
public async deliverAccessToken(orderId: string, accessToken: string, publisherAddress: string) {
return this.sendTransaction("deliverAccessToken", publisherAddress, [orderId, accessToken])
}
} }

View File

@ -8,7 +8,13 @@ const contracts: Map<string, Contract> = new Map<string, Contract>()
export default class ContractHandler { export default class ContractHandler {
public static async get(what: string): Contract { public static async get(what: string): Contract {
return contracts.get(what) || await ContractHandler.load(what) const where = (await (await Keeper.getInstance()).getNetworkName()).toLowerCase()
try {
return contracts.get(what) || await ContractHandler.load(what, where)
} catch (err) {
Logger.error("Failed to load", what, "from", where, err)
throw err
}
} }
public static async deployContracts() { public static async deployContracts() {
@ -48,21 +54,21 @@ export default class ContractHandler {
}) })
} }
private static async load(what: string): Promise<Contract> { private static async load(what: string, where: string): Promise<Contract> {
const web3 = Web3Provider.getWeb3() const web3 = Web3Provider.getWeb3()
const where = (await (await Keeper.getInstance()).getNetworkName()).toLowerCase() // Logger.log("Loading", what, "from", where)
Logger.log("Loading", what, "from", where)
try {
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${what}.${where}`) const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${what}.${where}`)
// Logger.log('Loaded artifact', artifact) // Logger.log('Loaded artifact', artifact)
const code = await web3.eth.getCode(artifact.address)
if (code === "0x0") {
// no code in the blockchain dude
throw new Error(`No code deployed at address ${artifact.address}, sorry.`)
}
// Logger.log("Getting instance of", what, "from", where, "at", artifact.address) // Logger.log("Getting instance of", what, "from", where, "at", artifact.address)
const contract = new web3.eth.Contract(artifact.abi, artifact.address) const contract = new web3.eth.Contract(artifact.abi, artifact.address)
Logger.log("Loaded", what, "from", where) Logger.log("Loaded", what, "from", where)
contracts.set(what, contract) contracts.set(what, contract)
return contracts.get(what) return contracts.get(what)
} catch (err) {
Logger.error("Failed to load", what, "from", where, err)
}
} }
private static replaceTokens(bytecode: string, tokens: any[]) { private static replaceTokens(bytecode: string, tokens: any[]) {

View File

@ -1,12 +1,13 @@
import Event from "web3" import Event from "web3"
import Contract from "web3-eth-contract" import Contract from "web3-eth-contract"
import Logger from "../utils/Logger"
import ContractHandler from "./ContractHandler" import ContractHandler from "./ContractHandler"
export default abstract class ContractWrapperBase { export default abstract class ContractWrapperBase {
protected static instance = null protected static instance = null
protected contract: Contract = null private contract: Contract = null
private contractName: string private contractName: string
constructor(contractName) { constructor(contractName) {
@ -46,6 +47,7 @@ export default abstract class ContractWrapperBase {
if (!this.contract.methods[name]) { if (!this.contract.methods[name]) {
throw new Error(`Method ${name} is not part of contract ${this.contractName}`) throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
} }
try {
const tx = this.contract.methods[name](...args) const tx = this.contract.methods[name](...args)
const gas = await tx.estimateGas(args, { const gas = await tx.estimateGas(args, {
from, from,
@ -54,6 +56,25 @@ export default abstract class ContractWrapperBase {
from, from,
gas, gas,
}) })
} catch (err) {
const argString = JSON.stringify(args, null, 2)
Logger.error(`Sending transaction ${name} on contract ${this.contractName} failed.`)
Logger.error(`Args: ${argString} From: ${from}`)
throw err
}
}
protected async call(name: string, args: any[], from?: string) {
if (!this.contract.methods[name]) {
throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
}
try {
const method = this.contract.methods[name](...args)
return method.call(from ? {from} : null)
} catch (err) {
Logger.error(`Calling method ${name} on contract ${this.contractName} failed. Args: ${args}`, err)
throw err
}
} }
} }

View File

@ -1,6 +1,5 @@
import BigNumber from "bignumber.js" import BigNumber from "bignumber.js"
import {Receipt} from "web3-utils" import {Receipt} from "web3-utils"
import ConfigProvider from "../ConfigProvider"
import Order from "../ocean/Order" import Order from "../ocean/Order"
import ContractWrapperBase from "./ContractWrapperBase" import ContractWrapperBase from "./ContractWrapperBase"
@ -14,50 +13,34 @@ export default class OceanMarket extends ContractWrapperBase {
// call functions (costs no gas) // call functions (costs no gas)
public async isAssetActive(assetId: string): Promise<boolean> { public async isAssetActive(assetId: string): Promise<boolean> {
return this.contract.methods.checkAsset(assetId).call() return this.call("checkAsset", [assetId])
} }
public async verifyOrderPayment(orderId: string): Promise<boolean> { public async verifyOrderPayment(orderId: string): Promise<boolean> {
return this.contract.methods.verifyPaymentReceived(orderId).call() return this.call("verifyPaymentReceived", [orderId])
} }
public async getAssetPrice(assetId: string): Promise<number> { public async getAssetPrice(assetId: string): Promise<number> {
return this.contract.methods.getAssetPrice(assetId) return this.call("getAssetPrice", [assetId])
.call()
.then((price: string) => new BigNumber(price).toNumber()) .then((price: string) => new BigNumber(price).toNumber())
} }
public async requestTokens(amount: number, receiverAddress: string): Promise<Receipt> { public async requestTokens(amount: number, receiverAddress: string): Promise<Receipt> {
return this.contract.methods.requestTokens(amount) return this.sendTransaction("requestTokens", receiverAddress, [amount])
.send({
from: receiverAddress,
})
} }
public async generateId(input: string): Promise<string> { public async generateId(input: string): Promise<string> {
return await this.contract.methods.generateId(input).call() return this.call("generateId", [input])
} }
public async register(assetId: string, price: number, publisherAddress: string): Promise<Receipt> { public async register(assetId: string, price: number, publisherAddress: string): Promise<Receipt> {
return await this.contract.methods.register(assetId, price) return this.sendTransaction("register", publisherAddress, [assetId, price])
.send({
from: publisherAddress,
gas: ConfigProvider.getConfig().defaultGas,
})
} }
public async payOrder(order: Order, payerAddreess: string): Promise<Receipt> { public async payOrder(order: Order, publisherAddress: string,
price: number, consumerAddress: string): Promise<Receipt> {
const args = [ return this.sendTransaction("sendPayment", consumerAddress, [
order.getId(), order.getAsset().publisher.getId(), order.getId(), publisherAddress, price, order.getTimeout(),
order.getAsset().price, order.getTimeout(), ])
]
return this.sendTransaction("sendPayment", payerAddreess, args)
}
public getAssetPublisher(assetId: string): Promise<string> {
return this.contract.methods.getAssetPublisher(assetId)
.call()
} }
} }

View File

@ -1,6 +1,5 @@
import BigNumber from "bignumber.js" import BigNumber from "bignumber.js"
import {Receipt} from "web3-utils" import {Receipt} from "web3-utils"
import ConfigProvider from "../ConfigProvider"
import ContractBaseWrapper from "./ContractWrapperBase" import ContractBaseWrapper from "./ContractWrapperBase"
export default class OceanToken extends ContractBaseWrapper { export default class OceanToken extends ContractBaseWrapper {
@ -12,16 +11,11 @@ export default class OceanToken extends ContractBaseWrapper {
} }
public async approve(marketAddress: string, price: number, buyerAddress: string): Promise<Receipt> { public async approve(marketAddress: string, price: number, buyerAddress: string): Promise<Receipt> {
return this.contract.methods.approve(marketAddress, price) return this.sendTransaction("approve", buyerAddress, [marketAddress, price])
.send({
from: buyerAddress,
gas: ConfigProvider.getConfig().defaultGas,
})
} }
public async balanceOf(address: string): Promise<number> { public async balanceOf(address: string): Promise<number> {
return this.contract.methods.balanceOf(address) return this.call("balanceOf", [address])
.call()
.then((balance: string) => new BigNumber(balance).toNumber()) .then((balance: string) => new BigNumber(balance).toNumber())
} }
} }

View File

@ -0,0 +1,9 @@
enum AccessStatus {
Requested,
Committed,
Delivered,
Verified,
Revoked,
}
export default AccessStatus

View File

@ -1,5 +1,4 @@
export default class Config { export default class Config {
public defaultGas: number = 300000
public providerUri: string public providerUri: string
public nodeUri: string public nodeUri: string
public web3Provider: any public web3Provider: any

View File

@ -1,30 +1,17 @@
import * as EthCrypto from "eth-crypto" import * as EthCrypto from "eth-crypto"
import EthEcies from "eth-ecies" import * as EthEcies from "eth-ecies"
import * as EthjsUtil from "ethereumjs-util" import * as EthjsUtil from "ethereumjs-util"
import JWT from "jsonwebtoken" import * as JWT from "jsonwebtoken"
import Keeper from "../keeper/Keeper" import Keeper from "../keeper/Keeper"
import Web3Provider from "../keeper/Web3Provider" import Web3Provider from "../keeper/Web3Provider"
import ProviderProvider from "../provider/ProviderProvider"
import Logger from "../utils/Logger" import Logger from "../utils/Logger"
import Account from "./Account" import Account from "./Account"
import OceanBase from "./OceanBase" import OceanBase from "./OceanBase"
import Order from "./Order" import Order from "./Order"
declare var fetch
export default class Asset extends OceanBase { export default class Asset extends OceanBase {
public static async load(assetId): Promise<Asset> {
const {market} = await Keeper.getInstance()
const asset = new Asset("unknown", "unknown",
await market.getAssetPrice(assetId),
new Account(await market.getAssetPublisher(assetId)))
asset.setId(assetId)
return asset
}
constructor(public name: string, constructor(public name: string,
public description: string, public description: string,
public price: number, public price: number,
@ -37,7 +24,7 @@ export default class Asset extends OceanBase {
return market.isAssetActive(this.getId()) return market.isAssetActive(this.getId())
} }
public async purchase(account: Account, timeout: number): Promise<Order> { public async purchase(consumer: Account, timeout: number): Promise<Order> {
const {token, market, auth} = await Keeper.getInstance() const {token, market, auth} = await Keeper.getInstance()
const key = EthCrypto.createIdentity() const key = EthCrypto.createIdentity()
@ -53,7 +40,7 @@ export default class Asset extends OceanBase {
try { try {
const marketAddr = market.getAddress() const marketAddr = market.getAddress()
// Allow market contract to transfer funds on the consumer"s behalf // Allow market contract to transfer funds on the consumer"s behalf
await token.approve(marketAddr, price, account.getId()) await token.approve(marketAddr, price, consumer.getId())
Logger.log(`${price} tokens approved on market with id: ${marketAddr}`) Logger.log(`${price} tokens approved on market with id: ${marketAddr}`)
} catch (err) { } catch (err) {
Logger.error("token.approve failed", err) Logger.error("token.approve failed", err)
@ -62,7 +49,7 @@ export default class Asset extends OceanBase {
try { try {
// Submit the access request // Submit the access request
const initiateAccessRequestReceipt = await auth.initiateAccessRequest(this, const initiateAccessRequestReceipt = await auth.initiateAccessRequest(this,
publicKey, timeout, account.getId()) publicKey, timeout, consumer.getId())
const {returnValues} = initiateAccessRequestReceipt.events.AccessConsentRequested const {returnValues} = initiateAccessRequestReceipt.events.AccessConsentRequested
Logger.log(`Keeper AccessConsentRequested event received on asset: ${this.getId()}`) Logger.log(`Keeper AccessConsentRequested event received on asset: ${this.getId()}`)
@ -73,91 +60,44 @@ export default class Asset extends OceanBase {
} }
return order return order
if (false) {
// todo: AccessRequestCommitted event is not emitted in this flow
await auth.listenToEventOnce(
"AccessRequestCommitted", {
filter: {
_id: order.getId(),
},
})
.then((accessRequestCommittedResult) => {
Logger.log("Got AccessRequestCommitted Event")
return order.pay(account)
})
.then((payAssetReceipt) => {
return auth.listenToEventOnce(
"EncryptedTokenPublished", {
filter: {
_id: order.getId(),
},
})
})
.then((encryptedTokenPublishedResult) => {
Logger.log("Got EncryptedTokenPublished Event")
const {returnValues} = encryptedTokenPublishedResult
return this.finalizePurchaseAsset(
returnValues._id, order, key, account,
)
})
}
} }
public async finalizePurchaseAsset(accessId: string, order: Order, key: any, account: Account): Promise<Order> { public async consume(order: Order, consumer: Account): Promise<string> {
const {auth} = await Keeper.getInstance() const {auth} = await Keeper.getInstance()
const encryptedAccessToken = await auth.getEncryptedAccessToken(accessId, this.getId()) const encryptedAccessToken = await auth.getEncryptedAccessToken(order.getId(), consumer.getId())
// grab the access token from acl contract // grab the access token from acl contract
const tokenNo0x = encryptedAccessToken.slice(2) const tokenNo0x = encryptedAccessToken.slice(2)
const encryptedTokenBuffer = Buffer.from(tokenNo0x, "hex") const encryptedTokenBuffer = Buffer.from(tokenNo0x, "hex")
const privateKey = key.privateKey.slice(2) const privateKey = order.getKey().privateKey.slice(2)
const accessTokenEncoded = EthEcies.Decrypt(Buffer.from(privateKey, "hex"), encryptedTokenBuffer) const accessTokenEncoded: string =
EthEcies.decrypt(Buffer.from(privateKey, "hex"), encryptedTokenBuffer).toString()
const accessToken = JWT.decode(accessTokenEncoded) // Returns a json object const accessToken = JWT.decode(accessTokenEncoded) // Returns a json object
// sign it if (!accessToken) {
const hexEncrToken = `0x${encryptedTokenBuffer.toString("hex")}` throw new Error(`AccessToken is not an jwt: ${accessTokenEncoded}`)
}
const signature = Web3Provider.getWeb3().eth.sign(account.getId(), hexEncrToken) const signature = Web3Provider.getWeb3().eth.sign(encryptedAccessToken, consumer.getId())
const fixedMsgSha = Web3Provider.getWeb3().utils.sha3(encryptedAccessToken) const encryptedAccessTokenSha3 = Web3Provider.getWeb3().utils.sha3(encryptedAccessToken)
// Download the data set from the provider using the url in the access token // Download the data set from the provider using the url in the access token
// decode the access token, grab the service_endpoint, request_id, // decode the access token, grab the service_endpoint, request_id,
// payload keys: ['consumerId', 'fixed_msg', 'sigEncJWT', 'jwt'] // payload keys: ['consumerId', 'fixed_msg', 'sigEncJWT', 'jwt']
const payload = JSON.stringify({ const payload = JSON.stringify({
consumerId: account.getId(), consumerId: consumer.getId(),
fixed_msg: fixedMsgSha, fixed_msg: encryptedAccessTokenSha3,
sigEncJWT: signature, sigEncJWT: signature,
jwt: accessTokenEncoded, jwt: accessTokenEncoded,
}) })
const accessUrl = await fetch(`${accessToken.service_endpoint}/${accessToken.resource_id}`, {
method: "POST",
body: payload,
headers: {
"Content-type": "application/json",
},
})
.then((response: any) => {
if (response.ok) {
return response.text()
}
Logger.log("Failed: ", response.status, response.statusText)
})
.then((consumptionUrl: string) => {
Logger.log("Success accessing consume endpoint: ", consumptionUrl)
return consumptionUrl
})
.catch((error) => {
Logger.error("Error fetching the data asset consumption url: ", error)
})
Logger.log("consume url: ", accessUrl)
order.setAccessUrl(accessUrl)
return order const accessUrl = await ProviderProvider.getProvider().getAccessUrl(accessToken, payload)
Logger.log("consume url: ", accessUrl)
return accessUrl
} }
} }

View File

@ -1,6 +1,8 @@
import ConfigProvider from "../ConfigProvider" import ConfigProvider from "../ConfigProvider"
import Keeper from "../keeper/Keeper" import Keeper from "../keeper/Keeper"
import Web3Provider from "../keeper/Web3Provider" import Web3Provider from "../keeper/Web3Provider"
import Provider from "../provider/Provider"
import ProviderProvider from "../provider/ProviderProvider"
import Logger from "../utils/Logger" import Logger from "../utils/Logger"
import Account from "./Account" import Account from "./Account"
import Asset from "./Asset" import Asset from "./Asset"
@ -12,6 +14,7 @@ export default class Ocean {
if (!Ocean.instance) { if (!Ocean.instance) {
ConfigProvider.configure(config) ConfigProvider.configure(config)
ProviderProvider.setProvider(Provider)
Ocean.instance = new Ocean(await Keeper.getInstance()) Ocean.instance = new Ocean(await Keeper.getInstance())
} }
@ -38,12 +41,16 @@ export default class Ocean {
// generate an id // generate an id
const assetId = await market.generateId(asset.name + asset.description) const assetId = await market.generateId(asset.name + asset.description)
Logger.log(`Registering: ${assetId} with price ${asset.price}`) Logger.log(`Registering: ${assetId} with price ${asset.price} for ${asset.publisher.getId()}`)
asset.setId(assetId) asset.setId(assetId)
const isAssetActive = await market.isAssetActive(assetId)
// register asset in the market // register asset in the market
if (!isAssetActive) {
const result = await market.register(asset.getId(), asset.price, asset.publisher.getId()) const result = await market.register(asset.getId(), asset.price, asset.publisher.getId())
Logger.log("Registered:", assetId, "in block", result.blockNumber) Logger.log("Registered:", assetId, "in block", result.blockNumber)
} else {
throw new Error("Asset already registered")
}
return assetId return assetId
} }
@ -68,12 +75,11 @@ export default class Ocean {
const {returnValues} = event const {returnValues} = event
const order: Order = new Order( const order: Order = new Order(
await Asset.load(returnValues._resourceId), null,
parseInt(returnValues._timeout, 10), parseInt(returnValues._timeout, 10),
null, null) null, null)
order.setId(returnValues._id) order.setId(returnValues._id)
order.setStatus(await auth.getOrderStatus(returnValues._id))
order.setPaid(await market.verifyOrderPayment(returnValues._id)) order.setPaid(await market.verifyOrderPayment(returnValues._id))
return order return order

View File

@ -1,4 +1,6 @@
import * as EthEcies from "eth-ecies"
import Keeper from "../keeper/Keeper" import Keeper from "../keeper/Keeper"
import AccessStatus from "../models/AccessStatus"
import Logger from "../utils/Logger" import Logger from "../utils/Logger"
import Account from "./Account" import Account from "./Account"
import Asset from "./Asset" import Asset from "./Asset"
@ -7,33 +9,15 @@ import OceanBase from "./OceanBase"
export default class Order extends OceanBase { export default class Order extends OceanBase {
private paid: boolean private paid: boolean
private status: number
private accessUrl: string
private accessId: string
constructor(private asset: Asset, private timeout: number, constructor(private asset: Asset, private timeout: number,
private pubkey: string, private key: any) { private pubkey: string, private key: any) {
super() super()
} }
public setAccessUrl(url: string) { public async getStatus(): Promise<AccessStatus> {
this.accessUrl = url const {auth} = await Keeper.getInstance()
} return auth.getOrderStatus(this.id)
public getAccessUrl() {
return this.accessUrl
}
public setStatus(status: number) {
this.status = status
}
public setAccessId(accessId: string) {
this.accessId = accessId
}
public getStatus() {
return this.status
} }
public setPaid(paid: boolean) { public setPaid(paid: boolean) {
@ -44,14 +28,6 @@ export default class Order extends OceanBase {
return this.paid return this.paid
} }
public getAsset() {
return this.asset
}
public getPubkey() {
return this.pubkey
}
public getTimeout() { public getTimeout() {
return this.timeout return this.timeout
} }
@ -60,12 +36,34 @@ export default class Order extends OceanBase {
return this.key return this.key
} }
public async pay(account: Account): Promise<string> { public async pay(consumer: Account): Promise<string> {
const {market} = await Keeper.getInstance() const {market} = await Keeper.getInstance()
Logger.log(`Sending payment: ${this.getId()} ${this.accessId} Logger.log(
${this.asset.publisher.getId()} ${this.asset.price} ${this.timeout}`) `Sending payment: ${this.getId()} ${this.asset.publisher.getId()} ${this.asset.price} ${this.timeout}`,
const payReceipt = await market.payOrder(this, account.getId()) )
const payReceipt = await market.payOrder(this, this.asset.publisher.getId(), this.asset.price, consumer.getId())
return payReceipt.events.PaymentReceived.returnValues._paymentId return payReceipt.events.PaymentReceived.returnValues._paymentId
} }
public async commit(accessToken: string) {
const {auth} = await Keeper.getInstance()
const commitAccessRequestReceipt = await auth.commitAccessRequest(this, this.asset.publisher.getId())
if (commitAccessRequestReceipt.events.AccessRequestRejected) {
const {returnValues} = commitAccessRequestReceipt.events.AccessRequestRejected
throw new Error(`commitAccessRequest failed ${JSON.stringify(returnValues, null, 2)}`)
}
const pubKey = await auth.getTempPubKey(this.getId())
if (this.pubkey !== pubKey) {
throw new Error("Pubkey missmatch")
}
const encryptedAccessToken =
EthEcies.encrypt(new Buffer(pubKey, "hex"), new Buffer(accessToken)).toString("hex")
await auth.deliverAccessToken(this.getId(), `0x${encryptedAccessToken}`, this.asset.publisher.getId())
}
} }

30
src/provider/Provider.ts Normal file
View File

@ -0,0 +1,30 @@
import fetch from "node-fetch"
import Logger from "../utils/Logger"
export default class Provider {
public static async getAccessUrl(accessToken: any, payload: any): Promise<string> {
const accessUrl = await fetch(`${accessToken.service_endpoint}/${accessToken.resource_id}`, {
method: "POST",
body: payload,
headers: {
"Content-type": "application/json",
},
})
.then((response: any) => {
if (response.ok) {
return response.text()
}
Logger.log("Failed: ", response.status, response.statusText)
})
.then((consumptionUrl: string) => {
Logger.log("Success accessing consume endpoint: ", consumptionUrl)
return consumptionUrl
})
.catch((error) => {
Logger.error("Error fetching the data asset consumption url: ", error)
})
return accessUrl
}
}

View File

@ -0,0 +1,14 @@
export default class ProviderProvider {
public static setProvider(provider) {
ProviderProvider.provider = provider
}
public static getProvider() {
return ProviderProvider.provider
}
private static provider
}

8
test/MockProvider.ts Normal file
View File

@ -0,0 +1,8 @@
import Provider from "../src/provider/Provider"
export default class MockProvider extends Provider {
public static async getAccessUrl(accessToken: any, payload: any): Promise<string> {
return "http://test/test"
}
}

View File

@ -13,7 +13,7 @@ describe("ContractHandler", () => {
describe("#get()", () => { describe("#get()", () => {
it("should load and get OceanToken correctly", async () => { it("should load and get OceanToken correctly", async () => {
assert(await ContractHandler.get("OceanTokewn") !== null) assert(await ContractHandler.get("OceanToken") !== null)
}) })
}) })

View File

@ -41,9 +41,9 @@ describe("Account", () => {
describe("#getEthBalance()", () => { describe("#getEthBalance()", () => {
it("should get initial balance", async () => { it("should get initial ether balance", async () => {
const account: Account = accounts[1] const account: Account = accounts[9]
const balance = await account.getEthBalance() const balance = await account.getEthBalance()
const web3 = Web3Provider.getWeb3() const web3 = Web3Provider.getWeb3()
@ -55,7 +55,7 @@ describe("Account", () => {
it("should get initial balance", async () => { it("should get initial balance", async () => {
const account: Account = accounts[1] const account: Account = accounts[9]
const balance = await account.getBalance() const balance = await account.getBalance()
const web3 = Web3Provider.getWeb3() const web3 = Web3Provider.getWeb3()

View File

@ -5,11 +5,15 @@ import Account from "../../src/ocean/Account"
import Asset from "../../src/ocean/Asset" import Asset from "../../src/ocean/Asset"
import Ocean from "../../src/ocean/Ocean" import Ocean from "../../src/ocean/Ocean"
import Order from "../../src/ocean/Order" import Order from "../../src/ocean/Order"
import ProviderProvider from "../../src/provider/ProviderProvider"
import config from "../config" import config from "../config"
import MockProvider from "../MockProvider"
const testName = "Test Asset 2" const testName = "Test Asset 2"
const testDescription = "This asset is pure owange" const testDescription = "This asset is pure owange"
const testPrice = 100 const testPrice = 100
const timeout = 100000
const accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1Mzk3ODcxMDEsImV4cCI6NDcyNjk5NjcwNCwiYXVkIjoiIiwic3ViIjoiIiwic2VydmljZV9lbmRwb2ludCI6Imh0dHA6Ly9hZGFzZCIsInJlc291cmNlX2lkIjoiMTIzNDUifQ.2H3TRC3CAToVE9divSckwHi_HNvgOHKrtJPo8128qrKBHTk7YYb0UNfVCuYqwhGR"
let ocean: Ocean let ocean: Ocean
let testAsset: Asset let testAsset: Asset
@ -18,6 +22,8 @@ let testPublisher: Account
before(async () => { before(async () => {
ConfigProvider.configure(config) ConfigProvider.configure(config)
ProviderProvider.setProvider(MockProvider)
await ContractHandler.deployContracts() await ContractHandler.deployContracts()
ocean = await Ocean.getInstance(config) ocean = await Ocean.getInstance(config)
accounts = await ocean.getAccounts() accounts = await ocean.getAccounts()
@ -50,16 +56,25 @@ describe("Asset", () => {
it("should purchase an asset", async () => { it("should purchase an asset", async () => {
// todo // todo
const order: Order = await testAsset.purchase(accounts[5], 10000) const consumerAccount = accounts[5]
const order: Order = await testAsset.purchase(consumerAccount, timeout)
assert(order) assert(order)
}) })
}) })
describe("#purchase()", () => { describe("#consume()", () => {
it("should purchase an asset", async () => { it("should consume an asset", async () => {
// todo const consumerAccount = accounts[5]
// await testAsset.finalizePurchaseAsset() await consumerAccount.requestTokens(testAsset.price)
// place order - consumer
const order: Order = await testAsset.purchase(consumerAccount, timeout)
// commit order - provider
await order.commit(accessToken)
// pay order - consumer
await order.pay(consumerAccount)
const url = await testAsset.consume(order, consumerAccount)
assert(url)
}) })
}) })
}) })

View File

@ -4,17 +4,27 @@ import ContractHandler from "../../src/keeper/ContractHandler"
import Account from "../../src/ocean/Account" import Account from "../../src/ocean/Account"
import Asset from "../../src/ocean/Asset" import Asset from "../../src/ocean/Asset"
import Ocean from "../../src/ocean/Ocean" import Ocean from "../../src/ocean/Ocean"
import Logger from "../../src/utils/Logger" import Order from "../../src/ocean/Order"
import config from "../config" import config from "../config"
let ocean: Ocean let ocean: Ocean
let accounts: Account[] let accounts: Account[]
let testAsset: Asset
let testPublisher: Account
const name = "Test Asset 3"
const description = "This asset is pure owange"
const price = 100
const timeout = 100000000
before(async () => { before(async () => {
ConfigProvider.configure(config) ConfigProvider.configure(config)
await ContractHandler.deployContracts() await ContractHandler.deployContracts()
ocean = await Ocean.getInstance(config) ocean = await Ocean.getInstance(config)
accounts = await ocean.getAccounts() accounts = await ocean.getAccounts()
testPublisher = accounts[0]
testAsset = new Asset(name, description, price, testPublisher)
}) })
describe("Ocean", () => { describe("Ocean", () => {
@ -36,15 +46,7 @@ describe("Ocean", () => {
it("should register an asset", async () => { it("should register an asset", async () => {
const publisher: Account = accounts[0] const assetId: string = await ocean.register(testAsset)
const name = "Test Asset 3"
const description = "This asset is pure owange"
const price = 100
const asset = new Asset(name, description, price, publisher)
const assetId: string = await ocean.register(asset)
assert(assetId.length === 66) assert(assetId.length === 66)
assert(assetId.startsWith("0x")) assert(assetId.startsWith("0x"))
@ -55,9 +57,17 @@ describe("Ocean", () => {
it("should list orders", async () => { it("should list orders", async () => {
// todo const testConsumer = accounts[1]
const orders = await ocean.getOrdersByConsumer(accounts[1]) const asset: Asset = new Asset("getOrdersByConsumer test", description, price, testPublisher)
Logger.log(orders)
await ocean.register(asset)
const order: Order = await asset.purchase(testConsumer, timeout)
const orders = await ocean.getOrdersByConsumer(testConsumer)
assert(orders.length === 1)
assert(orders[0].getId() === order.getId())
}) })
}) })

View File

@ -1,21 +1,24 @@
import * as assert from "assert" import * as assert from "assert"
import ConfigProvider from "../../src/ConfigProvider" import ConfigProvider from "../../src/ConfigProvider"
import ContractHandler from "../../src/keeper/ContractHandler" import ContractHandler from "../../src/keeper/ContractHandler"
import AccessStatus from "../../src/models/AccessStatus"
import Account from "../../src/ocean/Account" import Account from "../../src/ocean/Account"
import Asset from "../../src/ocean/Asset" import Asset from "../../src/ocean/Asset"
import Ocean from "../../src/ocean/Ocean" import Ocean from "../../src/ocean/Ocean"
import Order from "../../src/ocean/Order" import Order from "../../src/ocean/Order"
import Logger from "../../src/utils/Logger"
import config from "../config" import config from "../config"
const testName = "Test Asset 333" const testName = "Order Test Asset"
const testDescription = "This asset is pure owange" const testDescription = "This asset is pure owange"
const testPrice = 100 const testPrice = 100
const timeout = 1000000
const accessToken = "eyJhbGciOiJIUzI1"
let ocean: Ocean let ocean: Ocean
let testAsset: Asset let testAsset: Asset
let accounts: Account[] let accounts: Account[]
let testPublisher: Account let testPublisher: Account
let testConsumer: Account
before(async () => { before(async () => {
ConfigProvider.configure(config) ConfigProvider.configure(config)
@ -23,6 +26,7 @@ before(async () => {
ocean = await Ocean.getInstance(config) ocean = await Ocean.getInstance(config)
accounts = await ocean.getAccounts() accounts = await ocean.getAccounts()
testPublisher = accounts[0] testPublisher = accounts[0]
testConsumer = accounts[1]
// register an asset to play around with // register an asset to play around with
testAsset = new Asset(testName, testDescription, testPrice, testPublisher) testAsset = new Asset(testName, testDescription, testPrice, testPublisher)
await ocean.register(testAsset) await ocean.register(testAsset)
@ -30,17 +34,53 @@ before(async () => {
describe("Order", () => { describe("Order", () => {
describe("#pay()", () => { describe("#pay()", async () => {
it("should pay for the order", async () => { it("should pay for an order", async () => {
const order: Order = await testAsset.purchase(accounts[0], 10000) const order: Order = await testAsset.purchase(testConsumer, timeout)
assert(order) assert(order)
const paymentId: string = await order.pay(accounts[0]) await order.commit(accessToken)
Logger.log("paymentId", paymentId) 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)
})
})
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)
const status: AccessStatus = await order.getStatus()
assert(status === AccessStatus.Delivered)
})
})
}) })