new squid design

This commit is contained in:
Sebastian Gerske 2018-10-16 14:56:18 +02:00
parent fe1472208f
commit 19201ef6e7
28 changed files with 648 additions and 661 deletions

15
src/ConfigProvider.ts Normal file
View File

@ -0,0 +1,15 @@
import Config from "./models/Config"
export default class ConfigProvider {
public static getConfig() {
return ConfigProvider.config
}
public static configure(config: Config) {
ConfigProvider.config = config
}
private static config: Config
}

View File

@ -1,13 +1,11 @@
import {Receipt} from "web3-utils"
import Asset from "../models/Asset"
import Config from "../models/Config"
import Asset from "../ocean/Asset"
import ContractBaseWrapper from "./ContractWrapperBase"
import Web3Helper from "./Web3Helper"
export default class OceanAuth extends ContractBaseWrapper {
public static async getInstance(config: Config, web3Helper: Web3Helper): Promise<OceanAuth> {
const auth: OceanAuth = new OceanAuth(config, "OceanAuth", web3Helper)
public static async getInstance(): Promise<OceanAuth> {
const auth: OceanAuth = new OceanAuth("OceanAuth")
await auth.init()
return auth
}
@ -35,14 +33,11 @@ export default class OceanAuth extends ContractBaseWrapper {
public async initiateAccessRequest(asset: Asset, publicKey: string,
timeout: number, buyerAddress: string): Promise<Receipt> {
const args = [asset.assetId, asset.publisherId, publicKey, timeout]
const tx = this.contract.methods.initiateAccessRequest(...args)
const gas = await tx.estimateGas(args, {
from: buyerAddress,
})
return tx.send({
from: buyerAddress,
gas,
})
const args = [asset.getId(), asset.publisher.getId(), publicKey, timeout]
return this.sendTransaction("initiateAccessRequest", buyerAddress, args)
}
public async commitAccessRequest() {
// todo
}
}

View File

@ -1,19 +1,20 @@
import Contract from "web3-eth-contract"
import Logger from "../utils/Logger"
import Web3Helper from "./Web3Helper"
import Web3Provider from "./Web3Provider"
import Keeper from "./Keeper"
const contracts: Map<string, Contract> = new Map<string, Contract>()
export default class ContractHandler {
public static async get(what: string, web3Helper: Web3Helper): Contract {
return contracts.get(what) || await ContractHandler.load(what, web3Helper)
public static async get(what: string): Contract {
return contracts.get(what) || await ContractHandler.load(what)
}
public static async deployContracts(web3Helper: Web3Helper) {
public static async deployContracts() {
Logger.log("Trying to deploy contracts")
const web3 = web3Helper.getWeb3()
const web3 = Web3Provider.getWeb3()
const deployerAddress = (await web3.eth.getAccounts())[0]
@ -47,14 +48,14 @@ export default class ContractHandler {
})
}
private static async load(what: string, web3Helper: Web3Helper): Promise<Contract> {
const where = (await web3Helper.getNetworkName()).toLowerCase()
private static async load(what: string): Promise<Contract> {
const web3 = Web3Provider.getWeb3()
const where = (await (await Keeper.getInstance()).getNetworkName()).toLowerCase()
Logger.log("Loading", what, "from", where)
try {
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${what}.${where}`)
// Logger.log('Loaded artifact', artifact)
// Logger.log("Getting instance of", what, "from", where, "at", artifact.address)
const web3 = web3Helper.getWeb3()
const contract = new web3.eth.Contract(artifact.abi, artifact.address)
Logger.log("Loaded", what, "from", where)
contracts.set(what, contract)

View File

@ -1,25 +1,16 @@
import Event from "web3"
import Contract from "web3-eth-contract"
import Config from "../models/Config"
import ContractHandler from "./ContractHandler"
import Web3Helper from "./Web3Helper"
export default class ContractWrapperBase {
export default abstract class ContractWrapperBase {
public static async getInstance(config: Config, web3Helper: Web3Helper): Promise<any> {
// stub
}
protected static instance = null
protected contract: Contract = null
protected config: Config
protected web3Helper: Web3Helper
private contractName: string
constructor(config: Config, contractName: string, web3Helper: Web3Helper) {
this.config = config
constructor(contractName) {
this.contractName = contractName
this.web3Helper = web3Helper
}
public async listenToEventOnce(eventName: string, options: any): Promise<any> {
@ -48,7 +39,21 @@ export default class ContractWrapperBase {
}
protected async init() {
this.contract = await ContractHandler.get(this.contractName, this.web3Helper)
this.contract = await ContractHandler.get(this.contractName)
}
protected async sendTransaction(name: string, from: string, args: any[]) {
if (!this.contract.methods[name]) {
throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
}
const tx = this.contract.methods[name](...args)
const gas = await tx.estimateGas(args, {
from,
})
return tx.send({
from,
gas,
})
}
}

View File

@ -1,28 +1,53 @@
import Config from "../models/Config"
import OceanAuth from "./Auth"
import OceanMarket from "./Market"
import OceanToken from "./Token"
import Web3Helper from "./Web3Helper"
import Web3Provider from "./Web3Provider"
export default class Keeper {
public static async getInstance(config: Config, helper: Web3Helper) {
public static async getInstance() {
const contracts = new Keeper(helper)
if (Keeper.instance === null) {
Keeper.instance = new Keeper()
contracts.market = await OceanMarket.getInstance(config, helper)
contracts.auth = await OceanAuth.getInstance(config, helper)
contracts.token = await OceanToken.getInstance(config, helper)
return contracts
Keeper.instance.market = await OceanMarket.getInstance()
Keeper.instance.auth = await OceanAuth.getInstance()
Keeper.instance.token = await OceanToken.getInstance()
}
return Keeper.instance
}
public web3Helper: Web3Helper
private static instance: Keeper = null
public token: OceanToken
public market: OceanMarket
public auth: OceanAuth
private constructor(helper: Web3Helper) {
this.web3Helper = helper
public async getNetworkName(): Promise<string> {
return Web3Provider.getWeb3().eth.net.getId()
.then((networkId) => {
let network: string = "unknown"
switch (networkId) {
case 1:
network = "Main"
break
case 2:
network = "Morden"
break
case 3:
network = "Ropsten"
break
case 4:
network = "Rinkeby"
break
case 42:
network = "Kovan"
break
default:
network = "development"
}
return network
})
}
}

View File

@ -1,15 +1,13 @@
import BigNumber from "bignumber.js"
import {Receipt} from "web3-utils"
import Asset from "../models/Asset"
import Config from "../models/Config"
import Order from "../models/Order"
import ConfigProvider from "../ConfigProvider"
import Order from "../ocean/Order"
import ContractWrapperBase from "./ContractWrapperBase"
import Web3Helper from "./Web3Helper"
export default class OceanMarket extends ContractWrapperBase {
public static async getInstance(config: Config, web3Helper: Web3Helper): Promise<OceanMarket> {
const market: OceanMarket = new OceanMarket(config, "OceanMarket", web3Helper)
public static async getInstance(): Promise<OceanMarket> {
const market: OceanMarket = new OceanMarket("OceanMarket")
await market.init()
return market
}
@ -44,15 +42,22 @@ export default class OceanMarket extends ContractWrapperBase {
return await this.contract.methods.register(assetId, price)
.send({
from: publisherAddress,
gas: this.config.defaultGas,
gas: ConfigProvider.getConfig().defaultGas,
})
}
public async payAsset(asset: Asset, order: Order, buyerAddress: string): Promise<Receipt> {
return this.contract.methods.sendPayment(order.id, asset.publisherId, asset.price, order.timeout)
.send({
from: buyerAddress,
gas: this.config.defaultGas,
})
public async payOrder(order: Order, payerAddreess: string): Promise<Receipt> {
const args = [
order.getId(), order.getAsset().publisher.getId(),
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,13 +1,12 @@
import BigNumber from "bignumber.js"
import {Receipt} from "web3-utils"
import Config from "../models/Config"
import ConfigProvider from "../ConfigProvider"
import ContractBaseWrapper from "./ContractWrapperBase"
import Web3Helper from "./Web3Helper"
export default class OceanToken extends ContractBaseWrapper {
public static async getInstance(config: Config, web3Helper: Web3Helper): Promise<OceanToken> {
const token: OceanToken = new OceanToken(config, "OceanToken", web3Helper)
public static async getInstance(): Promise<OceanToken> {
const token: OceanToken = new OceanToken("OceanToken")
await token.init()
return token
}
@ -16,7 +15,7 @@ export default class OceanToken extends ContractBaseWrapper {
return this.contract.methods.approve(marketAddress, price)
.send({
from: buyerAddress,
gas: this.config.defaultGas,
gas: ConfigProvider.getConfig().defaultGas,
})
}

View File

@ -1,69 +0,0 @@
import * as Web3 from "web3"
import Config from "../models/Config"
import Logger from "../utils/Logger"
Logger.log("using web3", Web3.version)
export default class Web3Helper {
private web3: Web3
public constructor(config: Config) {
const web3Provider = config.web3Provider || new Web3.providers.HttpProvider(config.nodeUri)
this.web3 = new Web3(Web3.givenProvider || web3Provider)
}
public getWeb3() {
return this.web3
}
public getCurrentProvider() {
return this.web3.currentProvider
}
public async getAccounts(): Promise<any[]> {
return new Promise<any[]>((resolve, reject) => {
this.web3.eth.getAccounts((err: any, accounts: string[]) => {
if (err) {
reject(err)
throw err
}
resolve(accounts)
})
})
}
public async getNetworkName(): Promise<string> {
return this.web3.eth.net.getId()
.then((networkId) => {
let network: string = "unknown"
switch (networkId) {
case 1:
network = "Main"
break
case 2:
network = "Morden"
break
case 3:
network = "Ropsten"
break
case 4:
network = "Rinkeby"
break
case 42:
network = "Kovan"
break
default:
network = "development"
}
return network
})
}
// web3 wrappers
public sign(accountAddress: string, message: string) {
return this.web3.eth.sign(accountAddress, message)
}
}

View File

@ -0,0 +1,19 @@
import * as Web3 from "web3"
import ConfigProvider from "../ConfigProvider"
import Logger from "../utils/Logger"
Logger.log("using web3", Web3.version)
export default class Web3Provider {
public static getWeb3() {
if (Web3Provider.web3 === null) {
const config = ConfigProvider.getConfig()
const web3Provider = config.web3Provider || new Web3.providers.HttpProvider(config.nodeUri)
Web3Provider.web3 = new Web3(Web3.givenProvider || web3Provider)
}
return Web3Provider.web3
}
private static web3: Web3 = null
}

View File

@ -1,6 +0,0 @@
import Balance from "./Balance"
export default class Account {
public name: string
public balance: Balance
}

View File

@ -1,5 +0,0 @@
export default class Asset {
public assetId: string
public publisherId: string
public price: number
}

View File

@ -1,12 +0,0 @@
import Asset from "./Asset"
export default class Order {
public id: string
public asset: Asset
public assetId: string
public timeout: number
public pubkey: string
public key: any
public paid: boolean
public status: number
}

View File

@ -1,42 +1,40 @@
import BigNumber from "bignumber.js"
import AccountModel from "../models/Account"
import Keeper from "../keeper/Keeper"
import Web3Provider from "../keeper/Web3Provider"
import Balance from "../models/Balance"
import OceanBase from "./OceanBase"
export default class Account extends OceanBase {
private balance: Balance
public async getTokenBalance(accountAddress: string): Promise<number> {
return this.keeper.token.balanceOf(accountAddress)
public async getOceanBalance(): Promise<number> {
return (await Keeper.getInstance()).token.balanceOf(this.id)
}
public async getEthBalance(account: string): Promise<number> {
const {web3Helper} = this.keeper
public async getEthBalance(): Promise<number> {
// Logger.log("getting balance for", account);
return web3Helper.getWeb3().eth.getBalance(account, "latest")
.then((balance: string) => {
return Web3Provider.getWeb3().eth
.getBalance(this.id, "latest")
.then((balance: string): number => {
// Logger.log("balance", balance);
return new BigNumber(balance).toNumber()
})
}
public async list() {
const {web3Helper} = this.keeper
public async getBalance(): Promise<Balance> {
const ethAccounts = await web3Helper.getAccounts()
return Promise.all(ethAccounts
.map(async (account: string) => {
// await ocean.market.requestTokens(account, 1000)
return {
name: account,
balance: {
eth: await this.getEthBalance(account),
ocn: await this.getTokenBalance(account),
},
} as AccountModel
}))
if (!this.balance) {
this.balance = {
eth: await this.getEthBalance(),
ocn: await this.getOceanBalance(),
} as Balance
}
return this.balance
}
// Transactions with gas cost
public async requestTokens(amount: number, receiver: string): Promise<boolean> {
return this.keeper.market.requestTokens(amount, receiver)
public async requestTokens(amount: number): Promise<boolean> {
return (await Keeper.getInstance()).market.requestTokens(amount, this.id)
}
}

View File

@ -1,31 +1,163 @@
import AssetModel from "../models/Asset"
import * as EthCrypto from "eth-crypto"
import EthEcies from "eth-ecies"
import * as EthjsUtil from "ethereumjs-util"
import JWT from "jsonwebtoken"
import Keeper from "../keeper/Keeper"
import Web3Provider from "../keeper/Web3Provider"
import Logger from "../utils/Logger"
import Account from "./Account"
import OceanBase from "./OceanBase"
import Order from "./Order"
declare var fetch
export default class Asset extends OceanBase {
public async isAssetActive(assetId: string): Promise<boolean> {
const {market} = this.keeper
return market.isAssetActive(assetId)
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
}
public async registerAsset(name: string, description: string,
price: number, publisherAddress: string): Promise<AssetModel> {
const {market} = this.keeper
// generate an id
const assetId = await market.generateId(name + description)
Logger.log(`Registering: ${assetId} with price ${price}`)
// register asset in the market
const result = await market.register(assetId, price, publisherAddress)
Logger.log("Registered:", assetId, "in block", result.blockNumber)
return {
assetId,
publisherId: publisherAddress,
price,
} as AssetModel
constructor(public name: string,
public description: string,
public price: number,
public publisher: Account) {
super()
}
public async isActive(): Promise<boolean> {
const {market} = await Keeper.getInstance()
return market.isAssetActive(this.getId())
}
public async purchase(account: Account, timeout: number): Promise<Order> {
const {token, market, auth} = await Keeper.getInstance()
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())
Logger.log("The asset:", this.getId(), "is it valid?", isValid, "it's price is:", price)
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, account.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, account.getId())
const {returnValues} = initiateAccessRequestReceipt.events.AccessConsentRequested
Logger.log(`Keeper AccessConsentRequested event received on asset: ${this.getId()}`, returnValues)
order = new Order(this, returnValues._timeout, returnValues._pubKey, key)
order.setId(returnValues._id)
} catch (err) {
Logger.error("auth.initiateAccessRequest failed", err)
}
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> {
const {auth} = await Keeper.getInstance()
const encryptedAccessToken = await auth.getEncryptedAccessToken(accessId, this.getId())
// grab the access token from acl contract
const tokenNo0x = encryptedAccessToken.slice(2)
const encryptedTokenBuffer = Buffer.from(tokenNo0x, "hex")
const privateKey = key.privateKey.slice(2)
const accessTokenEncoded = EthEcies.Decrypt(Buffer.from(privateKey, "hex"), encryptedTokenBuffer)
const accessToken = JWT.decode(accessTokenEncoded) // Returns a json object
// sign it
const hexEncrToken = `0x${encryptedTokenBuffer.toString("hex")}`
const signature = Web3Provider.getWeb3().eth.sign(account.getId(), hexEncrToken)
const fixedMsgSha = Web3Provider.getWeb3().utils.sha3(encryptedAccessToken)
// Download the data set from the provider using the url in the access token
// decode the access token, grab the service_endpoint, request_id,
// payload keys: ['consumerId', 'fixed_msg', 'sigEncJWT', 'jwt']
const payload = JSON.stringify({
consumerId: account.getId(),
fixed_msg: fixedMsgSha,
sigEncJWT: signature,
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
}
}

View File

@ -1,45 +0,0 @@
import Config from "../models/Config"
import Logger from "../utils/Logger"
declare var fetch
export default class MetaData {
private assetsUrl: string
constructor(config: Config) {
const providerUri = config.providerUri || null
this.assetsUrl = providerUri + "/assets"
}
public getAssetsMetadata() {
return fetch(this.assetsUrl + "/metadata", {method: "GET"})
.then((res) => res.json())
.then((data) => JSON.parse(data))
}
public publishDataAsset(asset: object) {
return fetch(this.assetsUrl + "/metadata",
{
method: "POST",
body: JSON.stringify(asset),
headers: {"Content-type": "application/json"},
})
.then((response: any) => {
Logger.log("Success:", response)
if (response.ok) {
Logger.log("Success:", response)
return true
}
Logger.log("Failed: ", response.status, response.statusText)
return false
// throw new Error(response.statusText ? response.statusText :
// `publish asset failed with status ${response.status}`)
})
.catch((error: Error) => {
Logger.log(`Publish asset to ocean database could not be completed: ${error.message}`)
return false
})
}
}

View File

@ -1,36 +1,86 @@
import ConfigProvider from "../ConfigProvider"
import Keeper from "../keeper/Keeper"
import Web3Helper from "../keeper/Web3Helper"
import Config from "../models/Config"
import Web3Provider from "../keeper/Web3Provider"
import Logger from "../utils/Logger"
import Account from "./Account"
import Asset from "./Asset"
import MetaData from "./MetaData"
import Order from "./Order"
import Tribe from "./Tribe"
export default class Ocean {
public static async getInstance(config) {
const ocean = new Ocean(config)
ocean.keeper = await Keeper.getInstance(config, ocean.helper)
ocean.tribe = await Tribe.getInstance(ocean.helper)
ocean.order = new Order(ocean.keeper)
ocean.account = new Account(ocean.keeper)
ocean.asset = new Asset(ocean.keeper)
return ocean
if (!Ocean.instance) {
ConfigProvider.configure(config)
Ocean.instance = new Ocean()
}
return Ocean.instance
}
public account: Account
public order: Order
public tribe: Tribe
public asset: Asset
public helper: Web3Helper
public metadata: MetaData
private static instance = null
private keeper: Keeper
public async getAccounts(): Promise<Account[]> {
private constructor(config: Config) {
// retrieve eth accounts
const ethAccounts = await Web3Provider.getWeb3().eth.getAccounts()
this.helper = new Web3Helper(config)
this.metadata = new MetaData(config)
return ethAccounts
.map((address: string) => {
return new Account(address)
})
}
public async register(asset: Asset): Promise<Asset> {
const {market} = await Keeper.getInstance()
// generate an id
const assetId = await market.generateId(asset.name + asset.description)
Logger.log(`Registering: ${assetId} with price ${asset.price}`)
asset.setId(assetId)
// register asset in the market
const result = await market.register(asset.getId(), asset.price, asset.publisher.getId())
Logger.log("Registered:", assetId, "in block", result.blockNumber)
return asset
}
public async getOrdersByConsumer(consumer: Account): Promise<Order[]> {
const {auth, market} = await Keeper.getInstance()
Logger.log("Getting orders")
const accessConsentRequestedData = await auth.getEventData(
"AccessConsentRequested", {
filter: {
_consumer: consumer.getId(),
},
fromBlock: 0,
toBlock: "latest",
})
const orders = await Promise.all(
accessConsentRequestedData
.map(async (event: any) => {
const {returnValues} = event
const order: Order = new Order(
await Asset.load(returnValues._resourceId),
parseInt(returnValues._timeout, 10),
null, null)
order.setId(returnValues._id)
order.setStatus(await auth.getOrderStatus(returnValues._id))
order.setPaid(await market.verifyOrderPayment(returnValues._id))
return order
}),
)
// Logger.log("Got orders:", JSON.stringify(orders, null, 2))
Logger.log(`Got ${Object.keys(orders).length} orders`)
return orders
}
}

View File

@ -1,10 +1,18 @@
import Keeper from "../keeper/Keeper"
export default abstract class OceanBase {
export default class OceanBase {
protected id = "0x00"
protected keeper: Keeper
constructor(id?) {
if (id) {
this.id = id
}
}
constructor(keeper: Keeper) {
this.keeper = keeper
public getId() {
return this.id
}
public setId(id) {
this.id = id
}
}

View File

@ -1,198 +1,71 @@
import * as EthCrypto from "eth-crypto"
import EthEcies from "eth-ecies"
import * as EthjsUtil from "ethereumjs-util"
import JWT from "jsonwebtoken"
import Asset from "../models/Asset"
import OrderModel from "../models/Order"
import Keeper from "../keeper/Keeper"
import Logger from "../utils/Logger"
import Asset from "./Asset"
import OceanBase from "./OceanBase"
declare var fetch
import Account from "./Account"
export default class Order extends OceanBase {
private static create(asset: Asset, args, key): OrderModel {
const accessId = args._id
Logger.log(`got new access request id: ${accessId}`)
const order: OrderModel = {
id: accessId,
assetId: asset.assetId,
asset,
timeout: parseInt(args._timeout, 10),
pubkey: args._pubKey,
key,
} as OrderModel
// Logger.log("Created order", order)
private paid: boolean
private status: number
private accessUrl: string
private accessId: string
return order
constructor(private asset: Asset, private timeout: number,
private pubkey: string, private key: any) {
super()
}
public async getOrdersByConsumer(consumerAddress: string): Promise<OrderModel[]> {
const {auth, market} = this.keeper
Logger.log("Getting orders")
const accessConsentRequestedData = await auth.getEventData(
"AccessConsentRequested", {
filter: {
_consumer: consumerAddress,
},
fromBlock: 0,
toBlock: "latest",
})
const orders = await Promise.all(
accessConsentRequestedData
.filter((event: any) => {
return event.returnValues._consumer === consumerAddress
})
// todo: this is not orders model maybe? lacking proper typing here
.map(async (event: any) => ({
id: event.returnValues._id,
asset: null,
assetId: event.returnValues._resourceId,
timeout: parseInt(event.returnValues._timeout, 10),
pubkey: null,
key: null,
status: await auth.getOrderStatus(event.returnValues._id),
paid: await market.verifyOrderPayment(event.returnValues._id),
} as OrderModel
),
),
)
// Logger.log("Got orders:", JSON.stringify(orders, null, 2))
Logger.log(`Got ${Object.keys(orders).length} orders`)
return orders
public setAccessUrl(url: string) {
this.accessUrl = url
}
public async purchaseAsset(asset: Asset, timeout: number, buyerAddress: string): Promise<OrderModel> {
const {token, market, auth} = this.keeper
const key = EthCrypto.createIdentity()
const publicKey = EthjsUtil.privateToPublic(key.privateKey).toString("hex")
const price = await market.getAssetPrice(asset.assetId)
const isValid = await market.isAssetActive(asset.assetId)
Logger.log("The asset:", asset.assetId, "is it valid?", isValid, "it's price is:", price)
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, buyerAddress)
Logger.log(`${price} tokens approved on market with id: ${marketAddr}`)
} catch (err) {
Logger.error("token.approve failed", err)
}
let order: OrderModel
try {
// Submit the access request
const initiateAccessRequestReceipt = await auth.initiateAccessRequest(asset,
publicKey, timeout, buyerAddress)
const args = initiateAccessRequestReceipt.events.AccessConsentRequested.returnValues
Logger.log(`keeper AccessConsentRequested event received on asset: ${asset.assetId}`)
order = Order.create(asset, args, key)
} catch (err) {
Logger.error("auth.initiateAccessRequest failed", err)
}
return order
if (false) {
// todo: AccessRequestCommitted event is not emitted in this flow
await auth.listenToEventOnce(
"AccessRequestCommitted", {
filter: {
_id: order.id,
},
})
.then((accessRequestCommittedResult) => {
Logger.log("Got AccessRequestCommitted Event")
return this.payAsset(asset, accessRequestCommittedResult.returnValues, order, buyerAddress)
})
.then((payAssetReceipt) => {
return auth.listenToEventOnce(
"EncryptedTokenPublished", {
filter: {
_id: order.id,
},
})
})
.then((result) => {
Logger.log("Got EncryptedTokenPublished Event")
return this.finalizePurchaseAsset(
result, order, key, buyerAddress,
)
})
}
public getAccessUrl() {
return this.accessUrl
}
private async payAsset(asset: Asset, args, order, buyerAddress) {
const {market} = this.keeper
public setStatus(status: number) {
this.status = status
}
public setAccessId(accessId: string) {
Logger.log("accessId", accessId)
this.accessId = accessId
}
public getStatus() {
return this.status
}
public setPaid(paid: boolean) {
this.paid = paid
}
public getPaid() {
return this.paid
}
public getAsset() {
return this.asset
}
public getPubkey() {
return this.pubkey
}
public getTimeout() {
return this.timeout
}
public getKey() {
return this.key
}
public async pay(account: Account) {
const {market} = await Keeper.getInstance()
// send payment
Logger.log("Sending payment: ", order.id, args._id, asset.publisherId, asset.price, order.timeout)
return market.payAsset(asset, order, buyerAddress)
}
private async finalizePurchaseAsset(args, order, key, buyerAddress): Promise<OrderModel> {
const {auth, web3Helper} = this.keeper
const encryptedAccessToken = await auth.getEncryptedAccessToken(args._id, buyerAddress)
// grab the access token from acl contract
const tokenNo0x = encryptedAccessToken.slice(2)
const encryptedTokenBuffer = Buffer.from(tokenNo0x, "hex")
const privateKey = key.privateKey.slice(2)
const accessTokenEncoded = EthEcies.Decrypt(Buffer.from(privateKey, "hex"), encryptedTokenBuffer)
const accessToken = JWT.decode(accessTokenEncoded) // Returns a json object
// sign it
const hexEncrToken = `0x${encryptedTokenBuffer.toString("hex")}`
const signature = web3Helper.sign(buyerAddress, hexEncrToken)
const fixedMsgSha = web3Helper.getWeb3().utils.sha3(encryptedAccessToken)
// Download the data set from the provider using the url in the access token
// decode the access token, grab the service_endpoint, request_id,
// payload keys: ['consumerId', 'fixed_msg', 'sigEncJWT', 'jwt']
const payload = JSON.stringify({
consumerId: buyerAddress,
fixed_msg: fixedMsgSha,
sigEncJWT: signature,
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.accessUrl = accessUrl
return order
Logger.log("Sending payment: ", this.getId(), this.accessId,
this.asset.publisher.getId(), this.asset.price, this.timeout)
return market.payOrder(this, account.getId())
}
}

View File

@ -1,27 +0,0 @@
import Web3Helper from "../keeper/Web3Helper"
export default class Tribe {
public static getInstance(web3Helper: Web3Helper) {
return new Tribe(web3Helper)
}
private constructor(web3Helper: Web3Helper) {
}
// did ddo for tribes/marketplaces
public registerTribe() {
return ""
}
public tribessList() {
return ""
}
public resolveTribeDID() {
// verify DDO
return "DDO"
}
}

5
test/config.ts Normal file
View File

@ -0,0 +1,5 @@
import Config from "../src/models/Config"
export default {
nodeUri: "http://localhost:8545",
} as Config

View File

@ -0,0 +1,20 @@
import * as assert from "assert"
import ConfigProvider from "../../src/ConfigProvider"
import ContractHandler from "../../src/keeper/ContractHandler"
import config from "../config"
before(async () => {
ConfigProvider.configure(config)
await ContractHandler.deployContracts()
})
describe("ContractHandler", () => {
describe("#get()", () => {
it("should load and get OceanToken correctly", async () => {
assert(await ContractHandler.get("OceanTokewn") !== null)
})
})
})

View File

@ -1,18 +1,15 @@
import * as assert from "assert"
import ConfigProvider from "../../src/ConfigProvider"
import ContractHandler from "../../src/keeper/ContractHandler"
import Keeper from "../../src/keeper/Keeper"
import Web3Helper from "../../src/keeper/Web3Helper"
import Config from "../../src/models/Config"
import config from "../config"
let keeper: Keeper
before(async () => {
const config: Config = {
nodeUri: "http://localhost:8545",
} as Config
const web3Helper = new Web3Helper(config)
await ContractHandler.deployContracts(web3Helper)
keeper = await Keeper.getInstance(config, web3Helper)
ConfigProvider.configure(config)
await ContractHandler.deployContracts()
keeper = await Keeper.getInstance()
})
describe("Keeper", () => {
@ -30,9 +27,14 @@ describe("Keeper", () => {
it("should have token", () => {
assert(keeper.token !== null)
})
})
it("should have web3Helper", () => {
assert(keeper.web3Helper !== null)
describe("#getNetworkName()", () => {
it("should get development as default", async () => {
const networkName: string = await keeper.getNetworkName()
assert(networkName === "development")
})
})
})

View File

@ -1,44 +1,39 @@
import * as assert from "assert"
import ConfigProvider from "../../src/ConfigProvider"
import ContractHandler from "../../src/keeper/ContractHandler"
import Keeper from "../../src/keeper/Keeper"
import Web3Helper from "../../src/keeper/Web3Helper"
import Config from "../../src/models/Config"
import Web3Provider from "../../src/keeper/Web3Provider"
import Account from "../../src/ocean/Account"
import Ocean from "../../src/ocean/Ocean"
import config from "../config"
let keeper: Keeper
const config: Config = {
nodeUri: "http://localhost:8545",
} as Config
const web3Helper = new Web3Helper(config)
let ocean: Ocean
let accounts: Account[]
before(async () => {
await ContractHandler.deployContracts(web3Helper)
keeper = await Keeper.getInstance(config, web3Helper)
ConfigProvider.configure(config)
await ContractHandler.deployContracts()
ocean = await Ocean.getInstance(config)
accounts = await ocean.getAccounts()
})
describe("Account", () => {
describe("#getTokenBalance()", () => {
describe("#getOceanBalance()", () => {
it("should get initial balance", async () => {
it("should get initial ocean balance", async () => {
const account = new Account(keeper)
const accounts = await account.list()
const addr = accounts[1].name
const balance = await account.getTokenBalance(addr)
const balance = await accounts[0].getOceanBalance()
assert(0 === balance)
})
it("should get balance the correct balance", async () => {
it("should get the correct balance", async () => {
const account = new Account(keeper)
const amount: number = 100
const accounts = await account.list()
const addr = accounts[0].name
await account.requestTokens(amount, addr)
const balance = await account.getTokenBalance(addr)
const account: Account = accounts[0]
await account.requestTokens(amount)
const balance = await account.getOceanBalance()
assert(amount === balance)
})
@ -48,27 +43,24 @@ describe("Account", () => {
it("should get initial balance", async () => {
const account = new Account(keeper)
const accounts = await account.list()
const addr = accounts[5].name
const balance = await account.getEthBalance(addr)
const web3 = web3Helper.getWeb3()
const account: Account = accounts[1]
const balance = await account.getEthBalance()
const web3 = Web3Provider.getWeb3()
assert(Number(web3.utils.toWei("100", "ether")) === balance)
})
})
describe("#list()", () => {
describe("#getBalance()", () => {
it("should list accounts", async () => {
it("should get initial balance", async () => {
const account = new Account(keeper)
const accounts = await account.list()
const account: Account = accounts[1]
const balance = await account.getBalance()
const web3 = Web3Provider.getWeb3()
assert(10 === accounts.length)
assert(0 === accounts[5].balance.ocn)
assert("string" === typeof accounts[0].name)
assert(Number(web3.utils.toWei("100", "ether")) === balance.eth)
assert(0 === balance.ocn)
})
})
})

View File

@ -1,73 +1,63 @@
import * as assert from "assert"
import ConfigProvider from "../../src/ConfigProvider"
import ContractHandler from "../../src/keeper/ContractHandler"
import Keeper from "../../src/keeper/Keeper"
import Web3Helper from "../../src/keeper/Web3Helper"
import AssetModel from "../../src/models/Asset"
import Config from "../../src/models/Config"
import Account from "../../src/ocean/Account"
import Asset from "../../src/ocean/Asset"
import Ocean from "../../src/ocean/Ocean"
import config from "../config"
let keeper: Keeper
const testName = "Test Asset 2"
const testDescription = "This asset is pure owange"
const testPrice = 100
const config: Config = {
nodeUri: "http://localhost:8545",
} as Config
const web3Helper = new Web3Helper(config)
let ocean: Ocean
let testAsset: Asset
let accounts: Account[]
let testPublisher: Account
before(async () => {
await ContractHandler.deployContracts(web3Helper)
keeper = await Keeper.getInstance(config, web3Helper)
ConfigProvider.configure(config)
await ContractHandler.deployContracts()
ocean = await Ocean.getInstance(config)
accounts = await ocean.getAccounts()
testPublisher = accounts[0]
testAsset = new Asset(testName, testDescription, testPrice, testPublisher)
await ocean.register(testAsset)
})
describe("Asset", () => {
describe("#register()", () => {
it("should register asset", async () => {
const account = new Account(keeper)
const accounts = await account.list()
const addr = accounts[0].name
const name = "Test Asset"
const description = "This asset is pure owange"
const price = 100
const asset = new Asset(keeper)
const finalAsset: AssetModel = await asset.registerAsset(name, description, price, addr)
assert(finalAsset.assetId.length === 66)
assert(finalAsset.assetId.startsWith("0x"))
assert(finalAsset.publisherId === addr)
assert(finalAsset.price === price)
})
})
describe("#isAssetActive()", () => {
describe("#isActive()", () => {
it("should return true on new asset", async () => {
const account = new Account(keeper)
const accounts = await account.list()
const addr = accounts[0].name
const name = "Test Asset 2"
const description = "This asset is pure owange"
const price = 100
const asset = new Asset(keeper)
const finalAsset = await asset.registerAsset(name, description, price, addr)
const isAssetActive = await asset.isAssetActive(finalAsset.assetId)
const isAssetActive = await testAsset.isActive()
assert(true === isAssetActive)
})
it("should return false on unknown asset", async () => {
const asset = new Asset(keeper)
const isAssetActive = await asset.isAssetActive("0x0000")
const isAssetActive = await new Asset(testName, testDescription, testPrice, testPublisher)
.isActive()
assert(false === isAssetActive)
})
})
describe("#purchase()", () => {
it("should purchase an asset", async () => {
// todo
await testAsset.purchase(accounts[5], 10000)
})
})
describe("#purchase()", () => {
it("should purchase an asset", async () => {
// todo
// await testAsset.finalizePurchaseAsset()
})
})
})

View File

@ -1,43 +1,67 @@
import * as assert from "assert"
import ConfigProvider from "../../src/ConfigProvider"
import ContractHandler from "../../src/keeper/ContractHandler"
import Web3Helper from "../../src/keeper/Web3Helper"
import Config from "../../src/models/Config"
import Account from "../../src/ocean/Account"
import Asset from "../../src/ocean/Asset"
import Ocean from "../../src/ocean/Ocean"
import Logger from "../../src/utils/Logger"
import config from "../config"
let ocean: Ocean
let accounts: Account[]
before(async () => {
const config: Config = {
nodeUri: "http://localhost:8545",
} as Config
const web3Helper = new Web3Helper(config)
await ContractHandler.deployContracts(web3Helper)
ConfigProvider.configure(config)
await ContractHandler.deployContracts()
ocean = await Ocean.getInstance(config)
accounts = await ocean.getAccounts()
})
describe("Ocean", () => {
describe("public interface", () => {
describe("#getAccounts()", () => {
it("should have tribe", async () => {
it("should list accounts", async () => {
assert(ocean.tribe !== null)
const accs: Account[] = await ocean.getAccounts()
assert(10 === accs.length)
assert(0 === (await accs[5].getBalance()).ocn)
assert("string" === typeof accs[0].getId())
})
it("should have account", async () => {
})
assert(ocean.account !== null)
})
describe("#register()", () => {
it("should have order", async () => {
it("should register an asset", async () => {
assert(ocean.order !== null)
})
const publisher: Account = accounts[0]
it("should have asset", async () => {
const name = "Test Asset 3"
const description = "This asset is pure owange"
const price = 100
assert(ocean.asset !== null)
const asset = new Asset(name, description, price, publisher)
const finalAsset: Asset = await ocean.register(asset)
assert(finalAsset.getId().length === 66)
assert(finalAsset.getId().startsWith("0x"))
assert(finalAsset.publisher === publisher)
assert(finalAsset.price === price)
})
})
describe("#getOrdersByConsumer()", () => {
it("should list orders", async () => {
// todo
const orders = await ocean.getOrdersByConsumer(accounts[1])
Logger.log(orders)
})
})
})

View File

@ -0,0 +1,30 @@
import * as assert from "assert"
import OceanBase from "./OceanBaseMock"
describe("OceanBase", () => {
describe("#getId()", () => {
it("should get the id", async () => {
const id = "test"
const oceanBase = new OceanBase(id)
assert(oceanBase.getId() === id)
})
})
describe("#setId()", () => {
it("should get the id", async () => {
const id = "test"
const oceanBase = new OceanBase()
oceanBase.setId(id)
assert(oceanBase.getId() === id)
})
})
})

View File

@ -0,0 +1,5 @@
import OceanBase from "../../src/ocean/OceanBase"
export default class OceanBaseMock extends OceanBase {
}

View File

@ -1,86 +1,44 @@
import * as assert from "assert"
import ConfigProvider from "../../src/ConfigProvider"
import ContractHandler from "../../src/keeper/ContractHandler"
import Keeper from "../../src/keeper/Keeper"
import Web3Helper from "../../src/keeper/Web3Helper"
import AssetModel from "../../src/models/Asset"
import Config from "../../src/models/Config"
import OrderModel from "../../src/models/Order"
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"
let keeper: Keeper
let testAsset: AssetModel
let accounts
let buyerAddr
const testName = "Test Asset 333"
const testDescription = "This asset is pure owange"
const testPrice = 100
const config: Config = {
nodeUri: "http://localhost:8545",
} as Config
const web3Helper = new Web3Helper(config)
let ocean: Ocean
let testAsset: Asset
let accounts: Account[]
let testPublisher: Account
before(async () => {
await ContractHandler.deployContracts(web3Helper)
keeper = await Keeper.getInstance(config, web3Helper)
const account = new Account(keeper)
accounts = await account.list()
const sellerAddr = accounts[0].name
buyerAddr = accounts[2].name
const name = "Order Test Asset"
const description = "This asset is pure owange"
const price = 100
const asset = new Asset(keeper)
testAsset = await asset.registerAsset(name, description, price, sellerAddr)
// get tokens
await account.requestTokens(1000000000000000, buyerAddr)
ConfigProvider.configure(config)
await ContractHandler.deployContracts()
ocean = await Ocean.getInstance(config)
accounts = await ocean.getAccounts()
testPublisher = accounts[0]
// register an asset to play around with
testAsset = new Asset(testName, testDescription, testPrice, testPublisher)
await ocean.register(testAsset)
})
const timeout = 100000000000
describe("Order", () => {
describe("#purchaseAsset()", () => {
describe("#pay()", () => {
it("should purchase an asset", async () => {
it("should pay for the order", async () => {
const order = new Order(keeper)
const finalOrder: OrderModel = await order.purchaseAsset(testAsset, timeout, buyerAddr)
const order: Order = await testAsset.purchase(accounts[0], 10000)
assert(order)
assert(finalOrder.assetId === testAsset.assetId)
assert(finalOrder.asset.assetId === testAsset.assetId)
assert(finalOrder.timeout === timeout)
await order.pay(accounts[0])
})
})
describe("#getOrdersByConsumer()", () => {
it("should get orders by consumer if there is one", async () => {
const order = new Order(keeper)
const finalOrder: OrderModel = await order.purchaseAsset(testAsset, timeout, buyerAddr)
const orders: OrderModel[] = await order.getOrdersByConsumer(buyerAddr)
const datOrder = (await orders.filter((o) => o.id === finalOrder.id))[0]
assert(datOrder !== null)
assert(datOrder.assetId === testAsset.assetId)
assert(datOrder.timeout === timeout)
assert(datOrder.paid === true)
assert(datOrder.status === 0)
})
it("should return empty array if no orders found", async () => {
const order = new Order(keeper)
const orders: OrderModel[] = await order.getOrdersByConsumer(accounts[4].name)
assert(orders.length === 0)
})
})
})