From 5720fb620db3e996630b029d75b71fe61f18e10c Mon Sep 17 00:00:00 2001 From: Aitor <1726644+aaitor@users.noreply.github.com> Date: Thu, 23 Jun 2022 14:08:48 +0200 Subject: [PATCH] Fix in the retrieval of Provenance events (#292) * The provenance events were not retrieved properly, this PR fixes that * linting * adding method * Adjusting copyright to Nevermined AG * Managing objects depending on regular events or subgraph integration --- LICENSE | 2 +- NOTICE | 2 +- README.md | 2 +- integration/nevermined/Provenance.test.ts | 7 ++ package.json | 4 +- src/Instantiable.abstract.ts | 2 +- src/events/ContractEvent.ts | 29 +++---- src/events/NeverminedEvent.ts | 2 +- src/keeper/contracts/ContractBase.ts | 7 +- src/keeper/contracts/DIDRegistry.ts | 83 ++++++++++++++----- .../contracts/royalties/CurveRoyalties.ts | 4 +- .../contracts/royalties/RewardsDistributor.ts | 24 ++++-- .../royalties/RoyaltyScheme.abstract.ts | 8 +- .../contracts/royalties/StandardRoyalties.ts | 7 +- .../contracts/templates/AccessTemplate.ts | 3 +- src/nevermined/Assets.ts | 8 +- src/nevermined/Nfts.ts | 18 ++-- .../DistributeNFTCollateralCondition.test.ts | 6 +- test/mocks/ContractBase.Mock.ts | 7 +- 19 files changed, 157 insertions(+), 68 deletions(-) diff --git a/LICENSE b/LICENSE index 8c685705d..bbbcb5beb 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2020 Keyko GmbH + Copyright 2022 Nevermined AG This product includes software developed at BigchainDB GmbH and Ocean Protocol (https://www.oceanprotocol.com/) diff --git a/NOTICE b/NOTICE index 19e75d99c..f41aed98c 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Nevermined -Copyright 2020 Keyko GmbH. +Copyright 2022 Nevermined AG. This product includes software developed at BigchainDB GmbH and Ocean Protocol (https://www.oceanprotocol.com/). diff --git a/README.md b/README.md index c922744bd..e3fb33f3e 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ It keeps the same Apache v2 License and adds some improvements. See [NOTICE file ## License ``` -Copyright 2020 Keyko GmbH +Copyright 2022 Nevermined AG This product includes software developed at BigchainDB GmbH and Ocean Protocol (https://www.oceanprotocol.com/) diff --git a/integration/nevermined/Provenance.test.ts b/integration/nevermined/Provenance.test.ts index f14a8809e..b48dbb0f9 100644 --- a/integration/nevermined/Provenance.test.ts +++ b/integration/nevermined/Provenance.test.ts @@ -3,6 +3,7 @@ import { decodeJwt } from 'jose' import { config } from '../config' import { getMetadata } from '../utils' import { Nevermined, Account, DDO, ProvenanceMethod, utils } from '../../src' +import { sleep } from '../utils/utils' describe('Provenance', () => { let nevermined: Nevermined @@ -41,6 +42,7 @@ describe('Provenance', () => { metadata.userId = payload.sub ddo = await nevermined.assets.create(metadata, publisher) + await sleep(2000) const provenance = await nevermined.provenance.getProvenanceEntry(ddo.shortId()) assert.equal(utils.zeroX(provenance.did), utils.zeroX(ddo.shortId())) @@ -125,6 +127,10 @@ describe('Provenance', () => { it('should return the events associated to DID', async () => { const pm = ProvenanceMethod + + // wait for the graph to pickup the event + await sleep(3000) + const events = await nevermined.provenance.getDIDProvenanceEvents(ddo.shortId()) assert.deepEqual( @@ -140,6 +146,7 @@ describe('Provenance', () => { }) it('should return the events of an specific method by DID', async () => { + await sleep(2000) const events = await Promise.all( [ 'WAS_GENERATED_BY', diff --git a/package.json b/package.json index 7e9a9657c..2351071e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nevermined-io/nevermined-sdk-js", - "version": "0.20.1", + "version": "0.20.2", "description": "Javascript SDK for connecting with Nevermined Data Platform ", "main": "./dist/node/sdk.js", "typings": "./dist/node/sdk.d.ts", @@ -174,4 +174,4 @@ "browser": { "fs": false } -} \ No newline at end of file +} diff --git a/src/Instantiable.abstract.ts b/src/Instantiable.abstract.ts index a03b7770d..e5f806c7e 100644 --- a/src/Instantiable.abstract.ts +++ b/src/Instantiable.abstract.ts @@ -44,7 +44,7 @@ export abstract class Instantiable { * @return {Promise} Network ID. */ public async getNetworkId(): Promise { - if(!this.networkId) { + if (!this.networkId) { this.networkId = await this.web3.eth.net.getId() } diff --git a/src/events/ContractEvent.ts b/src/events/ContractEvent.ts index 3b95c7314..74e842c63 100644 --- a/src/events/ContractEvent.ts +++ b/src/events/ContractEvent.ts @@ -7,7 +7,7 @@ import { } from './NeverminedEvent' import ContractBase from '../keeper/contracts/ContractBase' import { KeeperError } from '../errors' -import { Nevermined } from "../nevermined/Nevermined"; +import { Nevermined } from '../nevermined/Nevermined' export class ContractEvent extends NeverminedEvent { public static getInstance( @@ -39,20 +39,21 @@ export class ContractEvent extends NeverminedEvent { } public async getPastEvents(options: EventOptions): EventResult { - const chainId = await this.getNetworkId() - - options.fromBlock = 0 - options.toBlock = 'latest' - - // Temporary workaround to work with mumbai - // Infura as a 1000 blokcs limit on their api - if (chainId === 80001 || chainId === 42) { - const latestBlock = await this.web3.eth.getBlockNumber() - options.fromBlock = latestBlock - 99 + try { + const chainId = await this.getNetworkId() + options.fromBlock = 0 + options.toBlock = 'latest' + + // Temporary workaround to work with mumbai + // Infura as a 1000 blokcs limit on their api + if (chainId === 80001 || chainId === 42) { + const latestBlock = await this.web3.eth.getBlockNumber() + options.fromBlock = latestBlock - 99 + } + return await this.getEventData(options) + } catch (error) { + return [] } - - const data = await this.getEventData(options) - return data } public async getBlockNumber(): Promise { diff --git a/src/events/NeverminedEvent.ts b/src/events/NeverminedEvent.ts index 667156b13..0c658f62f 100644 --- a/src/events/NeverminedEvent.ts +++ b/src/events/NeverminedEvent.ts @@ -1,5 +1,5 @@ import ContractBase from '../keeper/contracts/ContractBase' -import { Instantiable } from "../Instantiable.abstract"; +import { Instantiable } from '../Instantiable.abstract' export interface EventOptions { methodName?: string diff --git a/src/keeper/contracts/ContractBase.ts b/src/keeper/contracts/ContractBase.ts index ce06f15b8..7340b49dc 100644 --- a/src/keeper/contracts/ContractBase.ts +++ b/src/keeper/contracts/ContractBase.ts @@ -76,7 +76,12 @@ export abstract class ContractBase extends Instantiable { this.config.graphHttpUri ) } else { - this.events = ContractEvent.getInstance(this, eventEmitter, config.nevermined, this.web3) + this.events = ContractEvent.getInstance( + this, + eventEmitter, + config.nevermined, + this.web3 + ) } } diff --git a/src/keeper/contracts/DIDRegistry.ts b/src/keeper/contracts/DIDRegistry.ts index 332d2c51e..16b7181ee 100644 --- a/src/keeper/contracts/DIDRegistry.ts +++ b/src/keeper/contracts/DIDRegistry.ts @@ -39,7 +39,7 @@ export interface ProvenanceAttributeRegisteredEvent { relatedDid: string agentInvolvedId: string method: ProvenanceMethod - attributes: string + attributes?: string blockNumberUpdated: number } @@ -48,7 +48,7 @@ interface ProvenanceBaseEvent { method: ProvenanceMethod activityId: string provId: string - attributes: string + attributes?: string blockNumberUpdated: number } export interface WasGeneratedByEvent extends ProvenanceBaseEvent { @@ -367,41 +367,84 @@ export default class DIDRegistry extends ContractBase { } }) ) - .map( - ({ returnValues }) => - eventToObject(returnValues) as ProvenanceAttributeRegisteredEvent - ) + .map(event => { + if (event.returnValues === undefined) + return eventToObject(event) as ProvenanceAttributeRegisteredEvent + else + return eventToObject( + event.returnValues + ) as ProvenanceAttributeRegisteredEvent + }) .map(event => ({ ...event, method: +event.method })) + .sort( + ( + firstEvent: ProvenanceAttributeRegisteredEvent, + secondEvent: ProvenanceAttributeRegisteredEvent + ) => + Number(firstEvent.blockNumberUpdated) > + Number(secondEvent.blockNumberUpdated) + ? 1 + : -1 + ) } public async getDIDProvenanceMethodEvents( did: string, method: T ): Promise[]> { - const capitalize = string => - string.replace( - /([a-z]+)(?:_|$)/gi, - (_, w) => w.charAt(0).toUpperCase() + w.toLowerCase().slice(1) - ) let filter: any = { _did: didZeroX(did) } + let methodName = '' + let eventName = '' switch (method) { case ProvenanceMethod.ACTED_ON_BEHALF: + filter = { _entityDid: didZeroX(did) } + eventName = 'ActedOnBehalf' + methodName = 'getActedOnBehalfs' + break case ProvenanceMethod.WAS_ASSOCIATED_WITH: + eventName = 'WasAssociatedWith' + methodName = 'getWasAssociatedWiths' filter = { _entityDid: didZeroX(did) } break case ProvenanceMethod.WAS_DERIVED_FROM: + eventName = 'WasDerivedFrom' + methodName = 'getWasDerivedFroms' filter = { _usedEntityDid: didZeroX(did) } break + case ProvenanceMethod.USED: + eventName = 'Used' + methodName = 'getUseds' + break + case ProvenanceMethod.WAS_GENERATED_BY: + eventName = 'WasGeneratedBy' + methodName = 'getWasGeneratedBys' + break } - return ( - await this.events.getPastEvents({ - eventName: capitalize(ProvenanceMethod[method as any]), - methodName: `get${capitalize(ProvenanceMethod[method as any])}s`, - filterJsonRpc: filter, - filterSubgraph: { where: filter }, - result: {} - }) - ).map(({ returnValues }) => eventToObject(returnValues)) + const eventOptions = { + eventName, + methodName, + filterJsonRpc: filter, + filterSubgraph: { where: filter }, + result: { + provId: true, + _activityId: true, + _blockNumberUpdated: true + } + } + const events = await this.events.getPastEvents(eventOptions) + return events + .map(event => eventToObject(event)) + .map(event => ({ ...event, method: +method })) + .sort( + ( + firstEvent: ProvenanceAttributeRegisteredEvent, + secondEvent: ProvenanceAttributeRegisteredEvent + ) => + Number(firstEvent.blockNumberUpdated) > + Number(secondEvent.blockNumberUpdated) + ? 1 + : -1 + ) } public async getProvenanceEntry(provId: string) { diff --git a/src/keeper/contracts/royalties/CurveRoyalties.ts b/src/keeper/contracts/royalties/CurveRoyalties.ts index 3c58deec7..54d52121b 100644 --- a/src/keeper/contracts/royalties/CurveRoyalties.ts +++ b/src/keeper/contracts/royalties/CurveRoyalties.ts @@ -2,9 +2,7 @@ import { RoyaltyScheme } from './RoyaltyScheme.abstract' import { InstantiableConfig } from '../../../Instantiable.abstract' export class CurveRoyalties extends RoyaltyScheme { - public static async getInstance( - config: InstantiableConfig - ): Promise { + public static async getInstance(config: InstantiableConfig): Promise { return RoyaltyScheme.getInstance(config, 'CurveRoyalties', CurveRoyalties, true) } } diff --git a/src/keeper/contracts/royalties/RewardsDistributor.ts b/src/keeper/contracts/royalties/RewardsDistributor.ts index cf082114b..bb1df62d3 100644 --- a/src/keeper/contracts/royalties/RewardsDistributor.ts +++ b/src/keeper/contracts/royalties/RewardsDistributor.ts @@ -5,16 +5,25 @@ import { didZeroX, zeroX } from '../../../utils' import BigNumber from 'bignumber.js' export class RewardsDistributor extends ContractBase { - public static async getInstance(config: InstantiableConfig): Promise { + public static async getInstance( + config: InstantiableConfig + ): Promise { try { - const instance: RewardsDistributor = new RewardsDistributor('RewardsDistributor') + const instance: RewardsDistributor = new RewardsDistributor( + 'RewardsDistributor' + ) await instance.init(config, true) return instance } catch (e) { config.logger.warn('Cannot load optional contract RewardsDistributor') } } - public setReceivers(did: string, addr: string[], from?: Account, params?: TxParameters) { + public setReceivers( + did: string, + addr: string[], + from?: Account, + params?: TxParameters + ) { return this.sendFrom('setReceivers', [didZeroX(did), addr], from, params) } public claimReward( @@ -38,12 +47,9 @@ export class RewardsDistributor extends ContractBase { didZeroX(did), amountsString, receivers, - ...[ - returnAddress, - lockPaymentAddress, - tokenAddress, - lockCondition, - ].map(zeroX), + ...[returnAddress, lockPaymentAddress, tokenAddress, lockCondition].map( + zeroX + ), releaseConditions.map(zeroX) ], from, diff --git a/src/keeper/contracts/royalties/RoyaltyScheme.abstract.ts b/src/keeper/contracts/royalties/RoyaltyScheme.abstract.ts index 5a9bc501f..b3c3ae99b 100644 --- a/src/keeper/contracts/royalties/RoyaltyScheme.abstract.ts +++ b/src/keeper/contracts/royalties/RoyaltyScheme.abstract.ts @@ -19,12 +19,16 @@ export abstract class RoyaltyScheme extends ContractBase { } } - public setRoyalty(did: string, amount: number, from?: Account, params?: TxParameters) { + public setRoyalty( + did: string, + amount: number, + from?: Account, + params?: TxParameters + ) { return this.sendFrom('setRoyalty', [didZeroX(did), amount], from, params) } public async getRoyalty(did: string) { return Number(await this.call('royalties', [didZeroX(did)])) } - } diff --git a/src/keeper/contracts/royalties/StandardRoyalties.ts b/src/keeper/contracts/royalties/StandardRoyalties.ts index 7230c7518..1fc12b26c 100644 --- a/src/keeper/contracts/royalties/StandardRoyalties.ts +++ b/src/keeper/contracts/royalties/StandardRoyalties.ts @@ -5,6 +5,11 @@ export class StandardRoyalties extends RoyaltyScheme { public static async getInstance( config: InstantiableConfig ): Promise { - return RoyaltyScheme.getInstance(config, 'StandardRoyalties', StandardRoyalties, true) + return RoyaltyScheme.getInstance( + config, + 'StandardRoyalties', + StandardRoyalties, + true + ) } } diff --git a/src/keeper/contracts/templates/AccessTemplate.ts b/src/keeper/contracts/templates/AccessTemplate.ts index f3a149793..588aefecf 100644 --- a/src/keeper/contracts/templates/AccessTemplate.ts +++ b/src/keeper/contracts/templates/AccessTemplate.ts @@ -214,7 +214,7 @@ export class AccessTemplate extends BaseTemplate implements GenericAccess { * @param assetRewards rewards * @param consumer consumer * @param creator creator of the agreement - * @returns + * @returns */ public async escrowPaymentParams( agreementIdSeed: string, @@ -270,7 +270,6 @@ export class AccessTemplate extends BaseTemplate implements GenericAccess { lockPaymentConditionId[1], accessConditionId[1] ] - } private async createFullAgreementData( diff --git a/src/nevermined/Assets.ts b/src/nevermined/Assets.ts index a07478b50..39ceb387c 100644 --- a/src/nevermined/Assets.ts +++ b/src/nevermined/Assets.ts @@ -832,12 +832,16 @@ export class Assets extends Instantiable { this.logger.log('Mintable DID registred') const scheme = getRoyaltyScheme(this.nevermined, royaltyKind) observer.next(CreateProgressStep.SettingRoyaltyScheme) - await didRegistry.setDIDRoyalties(ddo.shortId(), scheme.address, publisher.getId(), txParams) + await didRegistry.setDIDRoyalties( + ddo.shortId(), + scheme.address, + publisher.getId(), + txParams + ) observer.next(CreateProgressStep.SettingRoyalties) await scheme.setRoyalty(ddo.shortId(), royalties, publisher, txParams) observer.next(CreateProgressStep.DidRegistered) - return storedDdo } catch (error) { throw new ApiError(error) diff --git a/src/nevermined/Nfts.ts b/src/nevermined/Nfts.ts index f84f378e6..aca2ff274 100644 --- a/src/nevermined/Nfts.ts +++ b/src/nevermined/Nfts.ts @@ -43,7 +43,7 @@ export class Nfts extends Instantiable { * @param {string} erc20TokenAddress The sales reward distribution. * @returns {DDO} The newly registered DDO. */ - public create( + public create( metadata: MetaData, publisher: Account, cap: number, @@ -463,13 +463,21 @@ export class Nfts extends Instantiable { */ public async details(did: string) { const details = await this.nevermined.keeper.didRegistry.getDIDRegister(did) - const royaltySchemeAddress = await this.nevermined.keeper.didRegistry.getDIDRoyalties(did) + const royaltySchemeAddress = await this.nevermined.keeper.didRegistry.getDIDRoyalties( + did + ) let royalties = Number(details[8]) let royaltyScheme = RoyaltyKind.Legacy - if (this.nevermined.keeper.royalties.curve && royaltySchemeAddress === this.nevermined.keeper.royalties.curve.address) { + if ( + this.nevermined.keeper.royalties.curve && + royaltySchemeAddress === this.nevermined.keeper.royalties.curve.address + ) { royaltyScheme = RoyaltyKind.Curve royalties = await this.nevermined.keeper.royalties.curve.getRoyalty(did) - } else if (this.nevermined.keeper.royalties.standard && royaltySchemeAddress === this.nevermined.keeper.royalties.standard.address) { + } else if ( + this.nevermined.keeper.royalties.standard && + royaltySchemeAddress === this.nevermined.keeper.royalties.standard.address + ) { royaltyScheme = RoyaltyKind.Standard royalties = await this.nevermined.keeper.royalties.standard.getRoyalty(did) } @@ -484,7 +492,7 @@ export class Nfts extends Instantiable { nftSupply: Number(details[6]), mintCap: Number(details[7]), royalties, - royaltyScheme, + royaltyScheme } } diff --git a/test/keeper/conditions/DistributeNFTCollateralCondition.test.ts b/test/keeper/conditions/DistributeNFTCollateralCondition.test.ts index 806d2e35b..3d00d2d76 100644 --- a/test/keeper/conditions/DistributeNFTCollateralCondition.test.ts +++ b/test/keeper/conditions/DistributeNFTCollateralCondition.test.ts @@ -36,7 +36,11 @@ describe('DistributeNFTCollateralCondition', () => { describe('#hashValues()', () => { it('should hash the values', async () => { - const hash = await condition.hashValues(didZeroX(did), vaultAddress, nftAddress) + const hash = await condition.hashValues( + didZeroX(did), + vaultAddress, + nftAddress + ) assert.match(hash, /^0x[a-f0-9]{64}$/i) const id = await condition.generateId(agreementId, hash) diff --git a/test/mocks/ContractBase.Mock.ts b/test/mocks/ContractBase.Mock.ts index 2e174dc51..5a921745a 100644 --- a/test/mocks/ContractBase.Mock.ts +++ b/test/mocks/ContractBase.Mock.ts @@ -5,7 +5,12 @@ export default class ContractBaseMock extends ContractBase { public async initMock(config: any) { await this.init(config) const eventEmitter = new EventHandler() - this.events = ContractEvent.getInstance(this, eventEmitter, this.nevermined, this.web3) + this.events = ContractEvent.getInstance( + this, + eventEmitter, + this.nevermined, + this.web3 + ) } public async callMock(name: string, args: any[], from?: string) {