diff --git a/package-lock.json b/package-lock.json index 2441c0d..6f923e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -129,9 +129,9 @@ } }, "@oceanprotocol/keeper-contracts": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/@oceanprotocol/keeper-contracts/-/keeper-contracts-0.3.23.tgz", - "integrity": "sha512-nuO5j27VJrx7OkqUrEI3b8PZOFOL1+M7IYFqUKHWZxmkUf+on6xW+oDV208AC9jymkfYADtgrgX2rc6nbq+7zQ==" + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@oceanprotocol/keeper-contracts/-/keeper-contracts-0.3.25.tgz", + "integrity": "sha512-lW/36WYYCIHSX4OVnbk6KcKnnV7vsBbrEQPW+6cWUDokEWoBfxpdMIeTxCOyKZKavv7MrtFHDrb8BgjfTIIhsA==" }, "@oceanprotocol/secret-store-client": { "version": "0.0.12", @@ -157,7 +157,7 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", "dev": true }, @@ -1435,7 +1435,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } @@ -1500,7 +1500,7 @@ "dependencies": { "file-type": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" }, "get-stream": { @@ -1514,7 +1514,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } @@ -3764,7 +3764,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { @@ -5519,7 +5519,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { @@ -6523,7 +6523,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "requires": { "block-stream": "0.0.9", @@ -7101,7 +7101,7 @@ }, "utf8": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.1.tgz", + "resolved": "http://registry.npmjs.org/utf8/-/utf8-2.1.1.tgz", "integrity": "sha1-LgHbAvfY0JRPdxBPFgnrDDBM92g=" }, "util": { diff --git a/package.json b/package.json index 39dcbb7..d051dc7 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ }, "homepage": "https://github.com/oceanprotocol/squid-js#readme", "dependencies": { - "@oceanprotocol/keeper-contracts": "0.3.23", + "@oceanprotocol/keeper-contracts": "0.3.25", "@oceanprotocol/secret-store-client": "0.0.12", "bignumber.js": "^8.0.1", "eth-crypto": "^1.2.7", @@ -65,7 +65,7 @@ "devDependencies": { "@types/chai": "^4.1.7", "@types/mocha": "^5.2.5", - "@types/node": "^10.12.7", + "@types/node": "^10.12.10", "chai": "^4.2.0", "mocha": "^5.2.0", "nyc": "^13.1.0", diff --git a/src/keeper/contracts/ContractBase.ts b/src/keeper/contracts/ContractBase.ts index f9eb9c4..a916d2a 100644 --- a/src/keeper/contracts/ContractBase.ts +++ b/src/keeper/contracts/ContractBase.ts @@ -60,16 +60,18 @@ export default abstract class ContractBase { if (!this.contract.methods[name]) { throw new Error(`Method "${name}" is not part of contract "${this.contractName}"`) } + // Logger.log(name, args) const method = this.contract.methods[name] try { - const tx = method(...args) - const estimatedGas = await tx.estimateGas(args, { + const methodInstance = method(...args) + const estimatedGas = await methodInstance.estimateGas(args, { from, }) - return tx.send({ + const tx = methodInstance.send({ from, gas: estimatedGas, }) + return tx } catch (err) { const mappedArgs = this.searchMethod(name).inputs.map((input, i) => { return { @@ -87,6 +89,7 @@ export default abstract class ContractBase { if (!this.contract.methods[name]) { throw new Error(`Method ${name} is not part of contract ${this.contractName}`) } + // Logger.log(name) try { const method = this.contract.methods[name](...args) return method.call(from ? {from} : null) diff --git a/src/keeper/contracts/ServiceAgreement.ts b/src/keeper/contracts/ServiceAgreement.ts index ecfcf4c..db09194 100644 --- a/src/keeper/contracts/ServiceAgreement.ts +++ b/src/keeper/contracts/ServiceAgreement.ts @@ -10,15 +10,18 @@ export default class ServiceAgreement extends ContractBase { return serviceAgreement } - public async setupAgreementTemplate(templateId: string, methodReflections: MethodReflection[], - dependencyMatrix: number[], name: any, fulfillmentIndices: number[], + public async setupAgreementTemplate(templateId: string, + methodReflections: MethodReflection[], + dependencyMatrix: number[], + name: any, + fulfillmentIndices: number[], fulfillmentOperator: number, - ownerAddress: string) - : Promise { + ownerAddress: string): Promise { return this.send("setupAgreementTemplate", ownerAddress, [ templateId, methodReflections.map((r) => r.address), - methodReflections.map((r) => r.signature), dependencyMatrix, name, fulfillmentIndices, fulfillmentOperator, + methodReflections.map((r) => r.signature), dependencyMatrix, name, fulfillmentIndices, + fulfillmentOperator, ]) } diff --git a/src/ocean/Ocean.ts b/src/ocean/Ocean.ts index cde057f..4784da7 100644 --- a/src/ocean/Ocean.ts +++ b/src/ocean/Ocean.ts @@ -13,7 +13,6 @@ import Service from "../ddo/Service" import Keeper from "../keeper/Keeper" import Web3Provider from "../keeper/Web3Provider" import Config from "../models/Config" -import InputType from "../models/InputType" import ValueType from "../models/ValueType" import SecretStoreProvider from "../secretstore/SecretStoreProvider" import Logger from "../utils/Logger" @@ -107,11 +106,11 @@ export default class Ocean { return null } - const parameters: Parameter[] = condition.methodReflection.inputs.map((input: InputType) => { + const parameters: Parameter[] = condition.parameters.map((parameter: Parameter) => { return { - name: input.name, - type: input.type, - value: mapParameterValueToName(input.name), + name: parameter.name, + type: parameter.type, + value: mapParameterValueToName(parameter.name), } as Parameter }) diff --git a/src/ocean/ServiceAgreements/Condition.ts b/src/ocean/ServiceAgreements/Condition.ts index d43996f..b8db3d6 100644 --- a/src/ocean/ServiceAgreements/Condition.ts +++ b/src/ocean/ServiceAgreements/Condition.ts @@ -1,4 +1,5 @@ import MethodReflection from "../../models/MethodReflection" +import Parameter from "./Parameter" export default class Condition { public methodReflection: MethodReflection @@ -7,4 +8,5 @@ export default class Condition { public dependencyTimeoutFlags: number[] public isTerminalCondition: boolean public timeout: number + public parameters: Parameter[] } diff --git a/src/ocean/ServiceAgreements/Method.ts b/src/ocean/ServiceAgreements/Method.ts index 4d8dbe6..1e31f46 100644 --- a/src/ocean/ServiceAgreements/Method.ts +++ b/src/ocean/ServiceAgreements/Method.ts @@ -1,8 +1,11 @@ +import Parameter from "./Parameter" + export default class Method { public name: string public contractName: string public methodName: string public timeout: number + public parameters: Parameter[] public dependencies: string[] public dependencyTimeoutFlags: number[] public isTerminalCondition: boolean diff --git a/src/ocean/ServiceAgreements/Parameter.ts b/src/ocean/ServiceAgreements/Parameter.ts new file mode 100644 index 0000000..ce82667 --- /dev/null +++ b/src/ocean/ServiceAgreements/Parameter.ts @@ -0,0 +1,4 @@ +export default class Parameter { + public name: string + public type: string +} diff --git a/src/ocean/ServiceAgreements/ServiceAgreement.ts b/src/ocean/ServiceAgreements/ServiceAgreement.ts index e296ec5..0f0302b 100644 --- a/src/ocean/ServiceAgreements/ServiceAgreement.ts +++ b/src/ocean/ServiceAgreements/ServiceAgreement.ts @@ -19,18 +19,12 @@ export default class ServiceAgreement extends OceanBase { // Logger.log("signing SA", serviceAgreementId) const service: Service = ddo.findServiceById(serviceDefinitionId) - const values: ValuePair[] = ServiceAgreement.getValuesFromService(service, serviceAgreementId) - const valueHashes = ServiceAgreement.createValueHashes(values) + const values: ValuePair[][] = ServiceAgreement.getValuesFromService(service, serviceAgreementId) + const valueHashes: string[] = ServiceAgreement.createValueHashes(values) const timeoutValues: number[] = ServiceAgreement.getTimeoutValuesFromService(service) - const serviceAgreementHashSignature = await ServiceAgreement - .createSAHashSignature( - service, - serviceAgreementId, - values, - valueHashes, - timeoutValues, - consumer) + const serviceAgreementHashSignature = await ServiceAgreement.createSAHashSignature(service, serviceAgreementId, + valueHashes, timeoutValues, consumer) return serviceAgreementHashSignature } @@ -46,8 +40,8 @@ export default class ServiceAgreement extends OceanBase { // Logger.log("executing SA", serviceAgreementId) const service: Service = ddo.findServiceById(serviceDefinitionId) - const values: ValuePair[] = ServiceAgreement.getValuesFromService(service, serviceAgreementId) - const valueHashes = ServiceAgreement.createValueHashes(values) + const values: ValuePair[][] = ServiceAgreement.getValuesFromService(service, serviceAgreementId) + const valueHashes: string[] = ServiceAgreement.createValueHashes(values) const timeoutValues: number[] = ServiceAgreement.getTimeoutValuesFromService(service) // todo get consumer from ddo @@ -60,11 +54,9 @@ export default class ServiceAgreement extends OceanBase { private static async createSAHashSignature(service: Service, serviceAgreementId: string, - values: ValuePair[], valueHashes: string[], timeoutValues: number[], - consumer: Account): - Promise { + consumer: Account): Promise { if (!service.templateId) { throw new Error("TemplateId not found in ddo.") @@ -74,13 +66,16 @@ export default class ServiceAgreement extends OceanBase { return condition.conditionKey }) - const serviceAgreementHash = ServiceAgreement - .hashServiceAgreement( - service.templateId, - serviceAgreementId, - conditionKeys, - valueHashes, - timeoutValues) + if (conditionKeys.length !== valueHashes.length) { + throw new Error("Hashing SA failed!") + } + + const serviceAgreementHash = ServiceAgreement.hashServiceAgreement( + service.templateId, + serviceAgreementId, + conditionKeys, + valueHashes, + timeoutValues) const serviceAgreementHashSignature = await Web3Provider .getWeb3().eth.sign(serviceAgreementHash, consumer.getId()) @@ -130,18 +125,31 @@ export default class ServiceAgreement extends OceanBase { ) } - private static createValueHashes(valuePairs: ValuePair[]): any[] { - return valuePairs.map((valuePair) => { - return ServiceAgreement.hashSingleValue(valuePair) + private static createValueHashes(parameterValuePairs: ValuePair[][]): string[] { + + const hashes: string[] = [] + parameterValuePairs.map((valuePairs: ValuePair[]) => { + + hashes.push(ServiceAgreement.hashValuePairArray(valuePairs)) }) + + return hashes } - private static hashSingleValue(data: ValuePair): string { + private static hashValuePairArray(valuePairs: ValuePair[]): string { + let hash: string try { - return Web3Provider.getWeb3().utils.soliditySha3(data).toString("hex") + hash = Web3Provider.getWeb3().utils.soliditySha3(...valuePairs).toString("hex") } catch (err) { - Logger.error(`Hashing of ${JSON.stringify(data, null, 2)} failed.`) + Logger.error(`Hashing of ${JSON.stringify(valuePairs, null, 2)} failed.`) + throw err } + + if (!hash) { + throw new Error("hashValuePairArray failed to create hash.") + } + + return hash } private static hashServiceAgreement(serviceAgreementTemplateId: string, @@ -168,20 +176,22 @@ export default class ServiceAgreement extends OceanBase { return timeoutValues } - private static getValuesFromService(service: Service, serviceAgreementId: string): ValuePair[] { + private static getValuesFromService(service: Service, serviceAgreementId: string): ValuePair[][] { - const values: ValuePair[] = [] + const values: ValuePair[][] = [] - service.conditions.forEach((condition) => { + service.conditions.forEach((condition, i) => { + const contionValues: ValuePair[] = [] condition.parameters.forEach((parameter) => { - values.push({ + + contionValues.push({ type: parameter.type, value: parameter.name === "serviceId" ? "0x" + serviceAgreementId : parameter.value, } as ValuePair) }) - }) - // Logger.log("Values", JSON.stringify(values, null, 2)) + values[i] = contionValues + }) return values } @@ -195,14 +205,16 @@ export default class ServiceAgreement extends OceanBase { super(serviceAgreementId) } - public async lockPayment(assetId: string, price: number, consumer: Account): Promise { - const {paymentConditions} = await Keeper.getInstance() + public async buyAsset(assetId: string, price: number, consumer: Account): Promise { + const {paymentConditions, token} = await Keeper.getInstance() - const lockPaymentRceipt = + await token.approve(paymentConditions.getAddress(), price, consumer.getId()) + + const lockPaymentReceipt = await paymentConditions.lockPayment(this.getId(), assetId, price, consumer.getId()) - return lockPaymentRceipt.status + return lockPaymentReceipt.status } public async grantAccess(assetId: string, documentId: string): Promise { @@ -212,7 +224,7 @@ export default class ServiceAgreement extends OceanBase { await accessConditions.grantAccess(this.getId(), assetId, documentId, this.publisher.getId()) - return grantAccessReceipt.status + return !!grantAccessReceipt.events.AccessGranted } public async getStatus() { diff --git a/src/ocean/ServiceAgreements/ServiceAgreementTemplate.ts b/src/ocean/ServiceAgreements/ServiceAgreementTemplate.ts index a51c64e..32317c5 100644 --- a/src/ocean/ServiceAgreements/ServiceAgreementTemplate.ts +++ b/src/ocean/ServiceAgreements/ServiceAgreementTemplate.ts @@ -12,8 +12,8 @@ import TemplateBase from "./Templates/TemplateBase" export default class ServiceAgreementTemplate extends OceanBase { - private static generateConditionsKey(serviceAgreementTemplateId: string, methodReflection: MethodReflection) - : string { + private static generateConditionsKey(serviceAgreementTemplateId: string, + methodReflection: MethodReflection): string { const values = [ {type: "bytes32", value: serviceAgreementTemplateId} as ValuePair, {type: "address", value: methodReflection.address} as ValuePair, @@ -35,13 +35,11 @@ export default class ServiceAgreementTemplate extends OceanBase { })) const fulfillmentIndices: number[] = this.template.Methods - .map((method: Method, i: number) => method.isTerminalCondition ? i : undefined) - .filter((index: number) => index !== undefined) + .map((method: Method, i: number) => method.isTerminalCondition ? i : null) + .filter((index: number) => index !== null) const {serviceAgreement} = await Keeper.getInstance() - const methodReflections = await this.getMethodReflections() - const owner = await this.getOwner() if (owner.getId() === templateOwnerAddress) { @@ -58,7 +56,7 @@ export default class ServiceAgreementTemplate extends OceanBase { const receipt = await serviceAgreement .setupAgreementTemplate( this.template.id, - methodReflections, + await this.getMethodReflections(), dependencyMatrix, Web3Provider.getWeb3().utils.fromAscii(this.template.templateName), fulfillmentIndices, @@ -106,6 +104,7 @@ export default class ServiceAgreementTemplate extends OceanBase { return { methodReflection, timeout: method.timeout, + parameters: method.parameters, dependencies: method.dependencies, dependencyTimeoutFlags: method.dependencyTimeoutFlags, isTerminalCondition: method.isTerminalCondition, @@ -123,23 +122,32 @@ export default class ServiceAgreementTemplate extends OceanBase { throw new Error("Deps and timeouts need the same length") } - // map name to index - const mappedDependencies: number[] = dependencies.map((dep: string) => { - return this.template.Methods.findIndex((m) => m.name === dep) + const mappedDependencies: number[] = [] + const mappedDependencyTimeoutFlags: number[] = [] + + this.template.Methods.forEach((m, i) => { + const di = dependencies.findIndex((d) => d === m.name) + mappedDependencies.push(di > -1 ? 1 : 0) + mappedDependencyTimeoutFlags.push((di > -1 && dependencyTimeoutFlags[di]) ? 1 : 0) }) - let compressedDependencyValue = 0 - const numBits = 2 // 1st for dependency, 2nd for timeout flag - for (let i = 0; i < mappedDependencies.length; i++) { - const dependencyIndex = mappedDependencies[i] - const timeout = dependencyTimeoutFlags[i] - const offset = i * numBits - // tslint:disable-next-line - compressedDependencyValue |= dependencyIndex * 2 ** (offset + 0) // the dependency bit - // tslint:disable-next-line - compressedDependencyValue |= timeout * 2 ** (offset + 1) // the timeout bit + if (mappedDependencies.length !== mappedDependencyTimeoutFlags.length) { + throw new Error("Deps and timeouts need the same length") } + // Logger.log(dependencies, mappedDependencies, dependencyTimeoutFlags, mappedDependencyTimeoutFlags) + + let compressedDependencyValue: number = 0 + const numBits: number = 2 // 1st for dependency, 2nd for timeout flag + mappedDependencies.forEach((d: number, i: number) => { + const t: number = mappedDependencyTimeoutFlags[i] + const offset: number = i * numBits + // tslint:disable-next-line + compressedDependencyValue |= d * 2 ** (offset + 0) // the dependency bit + // tslint:disable-next-line + compressedDependencyValue |= t * 2 ** (offset + 1) // the timeout bit + }) + return compressedDependencyValue } diff --git a/src/ocean/ServiceAgreements/Templates/Access.ts b/src/ocean/ServiceAgreements/Templates/Access.ts index 4fd13f5..8c41126 100644 --- a/src/ocean/ServiceAgreements/Templates/Access.ts +++ b/src/ocean/ServiceAgreements/Templates/Access.ts @@ -11,6 +11,16 @@ export default class Access extends TemplateBase { contractName: "PaymentConditions", methodName: "lockPayment", timeout: 0, + parameters: [ + { + name: "assetId", + type: "bytes32", + }, + { + name: "price", + type: "uint256", + }, + ], dependencies: [], dependencyTimeoutFlags: [], isTerminalCondition: false, @@ -20,6 +30,16 @@ export default class Access extends TemplateBase { contractName: "AccessConditions", methodName: "grantAccess", timeout: 10, + parameters: [ + { + name: "assetId", + type: "bytes32", + }, + { + name: "documentKeyId", + type: "bytes32", + }, + ], dependencies: ["lockPayment"], dependencyTimeoutFlags: [0], isTerminalCondition: false, @@ -29,6 +49,16 @@ export default class Access extends TemplateBase { contractName: "PaymentConditions", methodName: "releasePayment", timeout: 10, + parameters: [ + { + name: "assetId", + type: "bytes32", + }, + { + name: "price", + type: "uint256", + }, + ], dependencies: ["grantAccess"], dependencyTimeoutFlags: [0], isTerminalCondition: true, @@ -38,6 +68,16 @@ export default class Access extends TemplateBase { contractName: "PaymentConditions", methodName: "refundPayment", timeout: 10, + parameters: [ + { + name: "assetId", + type: "bytes32", + }, + { + name: "price", + type: "uint256", + }, + ], dependencies: ["lockPayment", "grantAccess"], dependencyTimeoutFlags: [0, 1], isTerminalCondition: true, diff --git a/test/ocean/ServiceAgreement.test.ts b/test/ocean/ServiceAgreement.test.ts index 8cbcdac..e944a8f 100644 --- a/test/ocean/ServiceAgreement.test.ts +++ b/test/ocean/ServiceAgreement.test.ts @@ -7,7 +7,6 @@ import EventHandlers from "../../src/ddo/EventHandlers" import MetaData from "../../src/ddo/MetaData" import Parameter from "../../src/ddo/Parameter" import Service from "../../src/ddo/Service" -import InputType from "../../src/models/InputType" import Account from "../../src/ocean/Account" import IdGenerator from "../../src/ocean/IdGenerator" import Ocean from "../../src/ocean/Ocean" @@ -49,58 +48,61 @@ describe("ServiceAgreement", () => { const conditions: Condition[] = await serviceAgreementTemplate.getConditions() // create ddo conditions out of the keys - const ddoConditions: DDOCondition[] = conditions.map((condition, index): DDOCondition => { + const ddoConditions: DDOCondition[] = conditions + .map((condition: Condition, index): DDOCondition => { - const events: Event[] = [ - { - name: "PaymentReleased", - actorType: [ - "consumer", - ], - handlers: { - moduleName: "serviceAgreement", - functionName: "fulfillAgreement", - version: "0.1", - } as EventHandlers, - } as Event, - ] + const events: Event[] = [ + { + name: "PaymentReleased", + actorType: [ + "consumer", + ], + handlers: { + moduleName: "serviceAgreement", + functionName: "fulfillAgreement", + version: "0.1", + } as EventHandlers, + } as Event, + ] - const mapParameterValueToName = (name) => { + const mapParameterValueToName = (name) => { - switch (name) { - case "price": - return metadata.base.price - case "assetId": - return "0x" + assetId - case "documentKeyId": - return "0x1234" + switch (name) { + case "price": + return metadata.base.price + case "assetId": + return "0x" + assetId + case "documentKeyId": + return "0x" + assetId + + } + + return null } - return null - } + const parameters: Parameter[] = condition.parameters + .map((parameter: Parameter) => { + return { + name: parameter.name, + type: parameter.type, + value: mapParameterValueToName(parameter.name), + } as Parameter + }) - const parameters: Parameter[] = condition.methodReflection.inputs.map((input: InputType) => { return { - name: input.name, - type: input.type, - value: mapParameterValueToName(input.name), - } as Parameter + contractName: condition.methodReflection.contractName, + methodName: condition.methodReflection.methodName, + timeout: condition.timeout, + index, + conditionKey: condition.condtionKey, + parameters, + events, + dependencies: condition.dependencies, + dependencyTimeoutFlags: condition.dependencyTimeoutFlags, + isTerminalCondition: condition.isTerminalCondition, + } as DDOCondition }) - return { - contractName: condition.methodReflection.contractName, - methodName: condition.methodReflection.methodName, - timeout: condition.timeout, - index, - conditionKey: condition.condtionKey, - parameters, - events, - dependencies: condition.dependencies, - dependencyTimeoutFlags: condition.dependencyTimeoutFlags, - isTerminalCondition: condition.isTerminalCondition, - } as DDOCondition - }) - accessService = { type: "Access", serviceDefinitionId: IdGenerator.generateId(), @@ -117,8 +119,7 @@ describe("ServiceAgreement", () => { describe("#signServiceAgreement()", () => { it("should sign an service agreement", async () => { - const id: string = IdGenerator.generateId() - const did: string = `did:op:${id}` + const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) const serviceAgreementId: string = IdGenerator.generateId() @@ -137,8 +138,7 @@ describe("ServiceAgreement", () => { describe("#executeServiceAgreement()", () => { it("should execute an service agreement", async () => { - const id: string = IdGenerator.generateId() - const did: string = `did:op:${id}` + const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) const serviceAgreementId: string = IdGenerator.generateId() @@ -162,8 +162,7 @@ describe("ServiceAgreement", () => { describe("#getStatus()", () => { it("should get the status of a newly created service agreement", async () => { - const id: string = IdGenerator.generateId() - const did: string = `did:op:${id}` + const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) const serviceAgreementId: string = IdGenerator.generateId() @@ -184,16 +183,16 @@ describe("ServiceAgreement", () => { }) }) - describe("#lockPayment()", () => { - xit("should lock the payment in that service agreement", async () => { + describe("#buyAsset()", () => { + it("should lock the payment in that service agreement", async () => { - const id: string = IdGenerator.generateId() - const did: string = `did:op:${id}` + const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService, metaDataService]}) const serviceAgreementId: string = IdGenerator.generateId() // @ts-ignore WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo)) + const serviceAgreementSignature: string = await ServiceAgreement.signServiceAgreement(assetId, ddo, accessService.serviceDefinitionId, serviceAgreementId, consumerAccount) @@ -204,17 +203,19 @@ describe("ServiceAgreement", () => { serviceAgreementId, serviceAgreementSignature, consumerAccount, publisherAccount) assert(serviceAgreement) - const paid: boolean = await serviceAgreement.lockPayment(assetId, metaDataService.metadata.base.price, + // get funds + await consumerAccount.requestTokens(metaDataService.metadata.base.price) + + const paid: boolean = await serviceAgreement.buyAsset(assetId, metaDataService.metadata.base.price, consumerAccount) assert(paid) }) }) describe("#grantAccess()", () => { - xit("should grant access in that service agreement", async () => { + it("should grant access in that service agreement", async () => { - const id: string = IdGenerator.generateId() - const did: string = `did:op:${id}` + const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) const serviceAgreementId: string = IdGenerator.generateId() @@ -230,11 +231,39 @@ describe("ServiceAgreement", () => { serviceAgreementId, serviceAgreementSignature, consumerAccount, publisherAccount) assert(serviceAgreement) - const paid: boolean = await serviceAgreement.lockPayment(assetId, 10, consumerAccount) + // get funds + await consumerAccount.requestTokens(metaDataService.metadata.base.price) + + const paid: boolean = await serviceAgreement.buyAsset(assetId, metaDataService.metadata.base.price, + consumerAccount) assert(paid) - const accessGranted: boolean = await serviceAgreement.grantAccess(assetId, IdGenerator.generateId()) + // todo: use document id + const accessGranted: boolean = await serviceAgreement.grantAccess(assetId, assetId) assert(accessGranted) }) + + it("should fail to grant grant access if there is no payment", async () => { + + const did: string = `did:op:${assetId}` + const ddo = new DDO({id: did, service: [accessService]}) + const serviceAgreementId: string = IdGenerator.generateId() + + // @ts-ignore + WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo)) + const serviceAgreementSignature: string = + await ServiceAgreement.signServiceAgreement(assetId, ddo, accessService.serviceDefinitionId, + serviceAgreementId, consumerAccount) + assert(serviceAgreementSignature) + + const serviceAgreement: ServiceAgreement = + await ServiceAgreement.executeServiceAgreement(assetId, ddo, accessService.serviceDefinitionId, + serviceAgreementId, serviceAgreementSignature, consumerAccount, publisherAccount) + assert(serviceAgreement) + + // todo: use document id + const accessGranted: boolean = await serviceAgreement.grantAccess(assetId, assetId) + assert(!accessGranted) + }) }) })