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:
parent
ae070be011
commit
c213a8d3ca
99
integration/ocean/Signature.test.ts
Normal file
99
integration/ocean/Signature.test.ts
Normal 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.",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
@ -38,19 +38,17 @@ export class DDO {
|
|||||||
* DID, descentralized ID.
|
* DID, descentralized ID.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
public id: string
|
public id: string = null
|
||||||
public created: string
|
public created: string
|
||||||
public publicKey: PublicKey[]
|
public publicKey: PublicKey[] = []
|
||||||
public authentication: Authentication[]
|
public authentication: Authentication[] = []
|
||||||
public service: Service[]
|
public service: Service[] = []
|
||||||
public proof: Proof
|
public proof: Proof
|
||||||
|
|
||||||
public constructor(ddo?: Partial<DDO>) {
|
public constructor(ddo?: Partial<DDO>) {
|
||||||
this.created = (ddo && ddo.created) || new Date().toISOString().replace(/\.[0-9]{3}/, "")
|
Object.assign(this, ddo, {
|
||||||
this.authentication = (ddo && ddo.authentication) || []
|
created: ddo.created || new Date().toISOString().replace(/\.[0-9]{3}/, ""),
|
||||||
this.id = (ddo && ddo.id) || null
|
})
|
||||||
this.publicKey = (ddo && ddo.publicKey) || []
|
|
||||||
this.service = (ddo && ddo.service) || []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public shortId(): string {
|
public shortId(): string {
|
||||||
|
@ -54,16 +54,46 @@ export default class OceanAgreements {
|
|||||||
serviceDefinitionId: string,
|
serviceDefinitionId: string,
|
||||||
consumer: Account,
|
consumer: Account,
|
||||||
): Promise<AgreementPreparionResult> {
|
): Promise<AgreementPreparionResult> {
|
||||||
|
|
||||||
const d: DID = DID.parse(did as string)
|
const d: DID = DID.parse(did as string)
|
||||||
const ddo = await AquariusProvider.getAquarius().retrieveDDO(d)
|
const ddo = await AquariusProvider.getAquarius().retrieveDDO(d)
|
||||||
const agreementId: string = generateId()
|
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}
|
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.
|
* Submit a service agreement to the publisher to create the agreement on-chain.
|
||||||
* @param {string} did Decentralized ID.
|
* @param {string} did Decentralized ID.
|
||||||
@ -82,10 +112,10 @@ export default class OceanAgreements {
|
|||||||
const result = await BrizoProvider
|
const result = await BrizoProvider
|
||||||
.getBrizo()
|
.getBrizo()
|
||||||
.initializeServiceAgreement(
|
.initializeServiceAgreement(
|
||||||
did,
|
didPrefixed(did),
|
||||||
agreementId,
|
zeroX(agreementId),
|
||||||
serviceDefinitionId,
|
serviceDefinitionId,
|
||||||
signature,
|
zeroX(signature),
|
||||||
consumer.getId(),
|
consumer.getId(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import { ServiceAgreementTemplateCondition } from "../../ddo/ServiceAgreementTemplate"
|
import { ServiceAgreementTemplateCondition } from "../../ddo/ServiceAgreementTemplate"
|
||||||
import { DDO } from "../../ddo/DDO"
|
import { DDO } from "../../ddo/DDO"
|
||||||
import { ServiceAccess } from "../../ddo/Service"
|
import { ServiceAccess } from "../../ddo/Service"
|
||||||
import Keeper from "../../keeper/Keeper"
|
|
||||||
import Web3Provider from "../../keeper/Web3Provider"
|
import Web3Provider from "../../keeper/Web3Provider"
|
||||||
import ValuePair from "../../models/ValuePair"
|
import ValuePair from "../../models/ValuePair"
|
||||||
import Logger from "../../utils/Logger"
|
import Logger from "../../utils/Logger"
|
||||||
import Account from "../Account"
|
import Account from "../Account"
|
||||||
import DID from "../DID"
|
import { signText, zeroX } from "../../utils"
|
||||||
import { signText } from "../../utils"
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: move this class to helpers, it only contains pure functions
|
// 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 service = ddo.findServiceById<"Access">(serviceDefinitionId)
|
||||||
const values: ValuePair[][] = ServiceAgreement.getValuesFromService(service, valuesMap)
|
const values: ValuePair[][] = ServiceAgreement.getValuesFromService(service, valuesMap)
|
||||||
const valueHashes: string[] = ServiceAgreement.createValueHashes(values)
|
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(
|
if (!service.templateId) {
|
||||||
service,
|
throw new Error("TemplateId not found in DDO.")
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceAgreementHashSignature = await ServiceAgreement.createHashSignature(
|
||||||
|
service.templateId,
|
||||||
serviceAgreementId,
|
serviceAgreementId,
|
||||||
valueHashes,
|
valueHashes,
|
||||||
|
timelockValues,
|
||||||
timeoutValues,
|
timeoutValues,
|
||||||
consumer,
|
consumer,
|
||||||
)
|
)
|
||||||
@ -39,23 +43,22 @@ export default class ServiceAgreement {
|
|||||||
return serviceAgreementHashSignature
|
return serviceAgreementHashSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async createSAHashSignature(
|
public static async createHashSignature(
|
||||||
service: ServiceAccess,
|
templateId: string,
|
||||||
serviceAgreementId: string,
|
serviceAgreementId: string,
|
||||||
valueHashes: string[],
|
valueHashes: string[],
|
||||||
|
timelockValues: number[],
|
||||||
timeoutValues: number[],
|
timeoutValues: number[],
|
||||||
consumer: Account,
|
consumer: Account,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
|
||||||
if (!service.templateId) {
|
|
||||||
throw new Error("TemplateId not found in ddo.")
|
|
||||||
}
|
|
||||||
|
|
||||||
const serviceAgreementHash = ServiceAgreement.hashServiceAgreement(
|
const serviceAgreementHash = ServiceAgreement.hashServiceAgreement(
|
||||||
service.templateId,
|
templateId,
|
||||||
serviceAgreementId,
|
serviceAgreementId,
|
||||||
valueHashes,
|
valueHashes,
|
||||||
timeoutValues)
|
timelockValues,
|
||||||
|
timeoutValues,
|
||||||
|
)
|
||||||
|
|
||||||
let serviceAgreementHashSignature = await signText(serviceAgreementHash, consumer.getId(), consumer.getPassword())
|
let serviceAgreementHashSignature = await signText(serviceAgreementHash, consumer.getId(), consumer.getPassword())
|
||||||
|
|
||||||
@ -83,26 +86,28 @@ export default class ServiceAgreement {
|
|||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
private static hashServiceAgreement(
|
public static hashServiceAgreement(
|
||||||
serviceAgreementTemplateId: string,
|
serviceAgreementTemplateId: string,
|
||||||
serviceAgreementId: string,
|
serviceAgreementId: string,
|
||||||
valueHashes: string[],
|
valueHashes: string[],
|
||||||
|
timelocks: number[],
|
||||||
timeouts: number[],
|
timeouts: number[],
|
||||||
): string {
|
): string {
|
||||||
|
|
||||||
const args = [
|
const args = [
|
||||||
{type: "bytes32", value: serviceAgreementTemplateId},
|
{type: "address", value: zeroX(serviceAgreementTemplateId)},
|
||||||
{type: "bytes32[]", value: valueHashes},
|
{type: "bytes32[]", value: valueHashes.map(zeroX)},
|
||||||
|
{type: "uint256[]", value: timelocks},
|
||||||
{type: "uint256[]", value: timeouts},
|
{type: "uint256[]", value: timeouts},
|
||||||
{type: "bytes32", value: "0x" + serviceAgreementId},
|
{type: "bytes32", value: zeroX(serviceAgreementId)},
|
||||||
]
|
]
|
||||||
|
|
||||||
return (Web3Provider as any).getWeb3().utils.soliditySha3(...args).toString("hex")
|
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
|
const timeoutValues: number[] = service.serviceAgreementTemplate.conditions
|
||||||
.map((condition: ServiceAgreementTemplateCondition) => condition.timeout)
|
.map((condition: ServiceAgreementTemplateCondition) => condition[type])
|
||||||
|
|
||||||
return timeoutValues
|
return timeoutValues
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user