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

Create Signature integration test and fixed/improved the generation of hashes.

This commit is contained in:
Pedro Gutiérrez 2019-03-11 22:52:53 +01:00 committed by Pedro Gutiérrez
parent ae070be011
commit c213a8d3ca
4 changed files with 166 additions and 34 deletions

View File

@ -0,0 +1,99 @@
import { assert } from "chai"
import { config } from "../config"
import { Ocean, Account, DDO, Keeper } from "../../src" // @oceanprotocol/squid
import ServiceAgreement from "../../src/ocean/ServiceAgreements/ServiceAgreement"
// WARN: not integration test. It has been done here because constant values
// depends on the first account on spree (only accessible from integration test)
describe("Signature", () => {
let consumer: Account
before(async () => {
await Ocean.getInstance(config)
// Accounts
consumer = new Account("0x00bd138abd70e2f00903268f3db08f2d25677c9e")
consumer.setPassword("node0")
})
it("should generate the correct signature", async () => {
const templateId = `0x${"f".repeat(40)}`
const agreementId = `0x${"e".repeat(64)}`
const accessId = `0x${"a".repeat(64)}`
const lockId = `0x${"b".repeat(64)}`
const escrowId = `0x${"c".repeat(64)}`
const hash = await ServiceAgreement.hashServiceAgreement(
templateId,
agreementId,
[accessId, lockId, escrowId],
[0, 0, 0],
[0, 0, 0],
)
assert.equal(hash, "0x67901517c18a3d23e05806fff7f04235cc8ae3b1f82345b8bfb3e4b02b5800c7", "The signatuere is not correct.")
})
it("should generate the correct signature", async () => {
const templates = (await Keeper.getInstance()).templates
const did = `did:op:${"c".repeat(64)}`
const templateId = `0x${"f".repeat(40)}`
const agreementId = `0x${"e".repeat(64)}`
const ddoOwner = `0x${"9".repeat(40)}`
const serviceDefinitionId = "0"
const amount = "10"
const accessId = `0x${"a".repeat(64)}`
const lockId = `0x${"b".repeat(64)}`
const serviceAgreementTemplate = await templates.escrowAccessSecretStoreTemplate.getServiceAgreementTemplate()
const ddo = new DDO({
id: did,
service: [
{
type: "Access",
purchaseEndpoint: undefined,
serviceEndpoint: undefined,
serviceDefinitionId,
templateId,
serviceAgreementTemplate,
} as any,
],
})
const valuesMap = {
rewardAddress: ddoOwner,
amount,
documentId: ddo.shortId(),
grantee: consumer.getId(),
receiver: consumer.getId(),
sender: ddoOwner,
lockCondition: lockId,
releaseCondition: accessId,
}
const signature = await ServiceAgreement.signServiceAgreement(
ddo,
serviceDefinitionId,
agreementId,
valuesMap,
consumer,
)
assert.equal(
signature,
// tslint:disable-next-line
"0x6bd49301a4a98d4e2ca149d649cc22fa0c5bd69269716d91c5cc17576ec3caef12a0edf611bb318e684683eec77b202bbbe484ceb698aec0e1250b7d1cf874dd1c",
"The signatuere is not correct.",
)
})
})

View File

@ -38,19 +38,17 @@ export class DDO {
* DID, descentralized ID.
* @type {string}
*/
public id: string
public id: string = null
public created: string
public publicKey: PublicKey[]
public authentication: Authentication[]
public service: Service[]
public publicKey: PublicKey[] = []
public authentication: Authentication[] = []
public service: Service[] = []
public proof: Proof
public constructor(ddo?: Partial<DDO>) {
this.created = (ddo && ddo.created) || new Date().toISOString().replace(/\.[0-9]{3}/, "")
this.authentication = (ddo && ddo.authentication) || []
this.id = (ddo && ddo.id) || null
this.publicKey = (ddo && ddo.publicKey) || []
this.service = (ddo && ddo.service) || []
Object.assign(this, ddo, {
created: ddo.created || new Date().toISOString().replace(/\.[0-9]{3}/, ""),
})
}
public shortId(): string {

View File

@ -54,16 +54,46 @@ export default class OceanAgreements {
serviceDefinitionId: string,
consumer: Account,
): Promise<AgreementPreparionResult> {
const d: DID = DID.parse(did as string)
const ddo = await AquariusProvider.getAquarius().retrieveDDO(d)
const agreementId: string = generateId()
const signature = await ServiceAgreement.signServiceAgreement(ddo, serviceDefinitionId, agreementId, consumer)
const valuesMap = await this.getValuesMapForPrepare(ddo, agreementId, consumer.getId())
const signature = await ServiceAgreement.signServiceAgreement(ddo, serviceDefinitionId, agreementId, valuesMap, consumer)
return {agreementId, signature}
}
// TODO: this map depends on the template, this generation should be reponsability of the template
private async getValuesMapForPrepare(ddo: DDO, agreementId: string, consumer: string): Promise<{[value: string]: string}> {
const keeper = await Keeper.getInstance()
const ddoOwner = ddo.proof && ddo.proof.creator
const amount = ddo.findServiceByType("Metadata").metadata.base.price
let lockCondition
let releaseCondition
try {
lockCondition = await keeper.conditions.lockRewardCondition.hashValues(ddoOwner, amount)
} catch(e) { }
try {
releaseCondition = await keeper.conditions.accessSecretStoreCondition.hashValues(ddo.shortId(), consumer)
} catch(e) { }
return {
rewardAddress: ddoOwner,
amount: amount.toString(),
documentId: ddo.shortId(),
grantee: consumer,
receiver: consumer,
sender: ddoOwner,
lockCondition,
releaseCondition,
}
}
/**
* Submit a service agreement to the publisher to create the agreement on-chain.
* @param {string} did Decentralized ID.
@ -82,10 +112,10 @@ export default class OceanAgreements {
const result = await BrizoProvider
.getBrizo()
.initializeServiceAgreement(
did,
agreementId,
didPrefixed(did),
zeroX(agreementId),
serviceDefinitionId,
signature,
zeroX(signature),
consumer.getId(),
)

View File

@ -1,13 +1,11 @@
import { ServiceAgreementTemplateCondition } from "../../ddo/ServiceAgreementTemplate"
import { DDO } from "../../ddo/DDO"
import { ServiceAccess } from "../../ddo/Service"
import Keeper from "../../keeper/Keeper"
import Web3Provider from "../../keeper/Web3Provider"
import ValuePair from "../../models/ValuePair"
import Logger from "../../utils/Logger"
import Account from "../Account"
import DID from "../DID"
import { signText } from "../../utils"
import { signText, zeroX } from "../../utils"
// TODO: move this class to helpers, it only contains pure functions
@ -24,12 +22,18 @@ export default class ServiceAgreement {
const service = ddo.findServiceById<"Access">(serviceDefinitionId)
const values: ValuePair[][] = ServiceAgreement.getValuesFromService(service, valuesMap)
const valueHashes: string[] = ServiceAgreement.createValueHashes(values)
const timeoutValues: number[] = ServiceAgreement.getTimeoutValuesFromService(service)
const timelockValues: number[] = ServiceAgreement.getTimeValuesFromService(service, "timelock")
const timeoutValues: number[] = ServiceAgreement.getTimeValuesFromService(service, "timeout")
const serviceAgreementHashSignature = await ServiceAgreement.createSAHashSignature(
service,
if (!service.templateId) {
throw new Error("TemplateId not found in DDO.")
}
const serviceAgreementHashSignature = await ServiceAgreement.createHashSignature(
service.templateId,
serviceAgreementId,
valueHashes,
timelockValues,
timeoutValues,
consumer,
)
@ -39,23 +43,22 @@ export default class ServiceAgreement {
return serviceAgreementHashSignature
}
private static async createSAHashSignature(
service: ServiceAccess,
public static async createHashSignature(
templateId: string,
serviceAgreementId: string,
valueHashes: string[],
timelockValues: number[],
timeoutValues: number[],
consumer: Account,
): Promise<string> {
if (!service.templateId) {
throw new Error("TemplateId not found in ddo.")
}
const serviceAgreementHash = ServiceAgreement.hashServiceAgreement(
service.templateId,
templateId,
serviceAgreementId,
valueHashes,
timeoutValues)
timelockValues,
timeoutValues,
)
let serviceAgreementHashSignature = await signText(serviceAgreementHash, consumer.getId(), consumer.getPassword())
@ -83,26 +86,28 @@ export default class ServiceAgreement {
return hash
}
private static hashServiceAgreement(
public static hashServiceAgreement(
serviceAgreementTemplateId: string,
serviceAgreementId: string,
valueHashes: string[],
timelocks: number[],
timeouts: number[],
): string {
const args = [
{type: "bytes32", value: serviceAgreementTemplateId},
{type: "bytes32[]", value: valueHashes},
{type: "address", value: zeroX(serviceAgreementTemplateId)},
{type: "bytes32[]", value: valueHashes.map(zeroX)},
{type: "uint256[]", value: timelocks},
{type: "uint256[]", value: timeouts},
{type: "bytes32", value: "0x" + serviceAgreementId},
{type: "bytes32", value: zeroX(serviceAgreementId)},
]
return (Web3Provider as any).getWeb3().utils.soliditySha3(...args).toString("hex")
}
private static getTimeoutValuesFromService(service: ServiceAccess): number[] {
private static getTimeValuesFromService(service: ServiceAccess, type: "timeout" | "timelock"): number[] {
const timeoutValues: number[] = service.serviceAgreementTemplate.conditions
.map((condition: ServiceAgreementTemplateCondition) => condition.timeout)
.map((condition: ServiceAgreementTemplateCondition) => condition[type])
return timeoutValues
}