diff --git a/package-lock.json b/package-lock.json index 500db652a..e3e39a9f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ocean-node", - "version": "3.1.11", + "version": "3.1.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocean-node", - "version": "3.1.11", + "version": "3.1.12", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index bca6ee73c..fd00bd054 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocean-node", - "version": "3.1.11", + "version": "3.1.12", "description": "Ocean Node is used to run all core services in the Ocean stack", "author": "Ocean Protocol Foundation", "license": "Apache-2.0", diff --git a/src/components/Indexer/processors/BaseProcessor.ts b/src/components/Indexer/processors/BaseProcessor.ts index 95fa4439a..939260be0 100644 --- a/src/components/Indexer/processors/BaseProcessor.ts +++ b/src/components/Indexer/processors/BaseProcessor.ts @@ -210,9 +210,14 @@ export abstract class BaseEventProcessor { } protected checkDdoHash(decryptedDocument: any, documentHashFromContract: any): boolean { - const utf8Bytes = toUtf8Bytes(JSON.stringify(decryptedDocument)) + const documentString = JSON.stringify(decryptedDocument) + const utf8Bytes = toUtf8Bytes(documentString) const expectedMetadata = hexlify(utf8Bytes) - if (create256Hash(expectedMetadata.toString()) !== documentHashFromContract) { + const validHashes = [ + create256Hash(expectedMetadata.toString()), + create256Hash(documentString) + ] + if (!validHashes.includes(documentHashFromContract)) { INDEXER_LOGGER.error(`DDO checksum does not match.`) return false } @@ -317,6 +322,9 @@ export abstract class BaseEventProcessor { chainId, decrypterAddress: ethAddress, dataNftAddress: contractAddress, + encryptedDocument: txId ? undefined : metadata, + flags: parseInt(flag), + documentHash: metadataHash || undefined, signature, nonce } @@ -383,7 +391,7 @@ export abstract class BaseEventProcessor { ddo = JSON.parse(response.data) responseHash = create256Hash(ddo) } - if (responseHash !== metadataHash) { + if (metadataHash && responseHash !== metadataHash) { const msg = `Hash check failed: response=${ddo}, decrypted ddo hash=${responseHash}\n metadata hash=${metadataHash}` INDEXER_LOGGER.log(LOG_LEVELS_STR.LEVEL_ERROR, msg) throw new Error(msg) @@ -429,6 +437,7 @@ export abstract class BaseEventProcessor { decrypterAddress: ethAddress, chainId, encryptedDocument: metadata, + flags: parseInt(flag), documentHash: metadataHash, dataNftAddress: contractAddress, signature, @@ -491,6 +500,7 @@ export abstract class BaseEventProcessor { decrypterAddress: ethAddress, chainId, encryptedDocument: metadata, + flags: parseInt(flag), documentHash: metadataHash, dataNftAddress: contractAddress, signature, diff --git a/src/components/Indexer/processors/MetadataEventProcessor.ts b/src/components/Indexer/processors/MetadataEventProcessor.ts index 82b6162f1..f618c85c7 100644 --- a/src/components/Indexer/processors/MetadataEventProcessor.ts +++ b/src/components/Indexer/processors/MetadataEventProcessor.ts @@ -23,6 +23,16 @@ import { getConfiguration } from '../../../utils/config.js' import { isRemoteDDO } from '../../core/utils/validateDdoHandler.js' export class MetadataEventProcessor extends BaseEventProcessor { + private isDDO(data: any): data is Record { + return ( + data && + typeof data === 'object' && + !Array.isArray(data) && + typeof data.id === 'string' && + typeof data.version === 'string' + ) + } + async processEvent( event: ethers.Log, chainId: number, @@ -122,25 +132,57 @@ export class MetadataEventProcessor extends BaseEventProcessor { metadataHash, metadata ) + const isRemoteMetadata = isRemoteDDO(decryptDDO) + const isEncryptedMetadata = (parseInt(flag) & 2) !== 0 let ddo = await this.processDDO(decryptDDO) - if ( - !isRemoteDDO(decryptDDO) && - parseInt(flag) !== 2 && - !this.checkDdoHash(ddo, metadataHash) - ) { + if (!isEncryptedMetadata && !this.checkDdoHash(ddo, metadataHash)) { return } if (ddo.encryptedData) { + let { encryptedData } = ddo + if ((parseInt(flag) & 2) !== 0) { + try { + const decryptedIpfsPayload = await this.decryptDDO( + decodedEventData.args[2], + flag, + owner, + event.address, + chainId, + '', + '', + ddo.encryptedData + ) + encryptedData = decryptedIpfsPayload.encryptedData || encryptedData + } catch (error) { + INDEXER_LOGGER.log( + LOG_LEVELS_STR.LEVEL_ERROR, + `Unable to decrypt encrypted IPFS DDO payload, trying plaintext payload fallback: ${ + error instanceof Error ? error.message : String(error) + }` + ) + } + } + const proof = await this.decryptDDOIPFS( decodedEventData.args[2], owner, - ddo.encryptedData + encryptedData ) - const data = this.getDataFromProof(proof) - const ddoInstance = DDOManager.getDDOClass(data.ddoObj) - ddo = ddoInstance.updateFields({ - proof: { signature: data.signature, header: data.header } - }) + const data = typeof proof === 'string' ? this.getDataFromProof(proof) : null + + const ddoObj = data?.ddoObj || (this.isDDO(proof) ? proof : null) + if (!ddoObj) { + throw new Error( + 'IPFS encryptedData payload is neither a DDO nor a supported DDO proof.' + ) + } + const ddoInstance = DDOManager.getDDOClass(ddoObj) + ddo = + data?.signature && data?.header + ? ddoInstance.updateFields({ + proof: { signature: data.signature, header: data.header } + }) + : ddoInstance.getDDOData() } const clonedDdo = structuredClone(ddo) const updatedDdo = deleteIndexedMetadataIfExists(clonedDdo) @@ -159,8 +201,12 @@ export class MetadataEventProcessor extends BaseEventProcessor { ) return } - // for unencrypted DDOs - if ((parseInt(flag) & 2) === 0 && !this.checkDdoHash(updatedDdo, metadataHash)) { + // for unencrypted inline DDOs + if ( + !isRemoteMetadata && + !isEncryptedMetadata && + !this.checkDdoHash(updatedDdo, metadataHash) + ) { INDEXER_LOGGER.error('Unencrypted DDO hash does not match metadata hash.') await ddoState.update( this.networkId, diff --git a/src/components/core/handler/ddoHandler.ts b/src/components/core/handler/ddoHandler.ts index 13f281219..b33e5fedc 100644 --- a/src/components/core/handler/ddoHandler.ts +++ b/src/components/core/handler/ddoHandler.ts @@ -357,7 +357,7 @@ export class DecryptDdoHandler extends CommandHandler { } else { // checksum matches const decryptedDocumentHash = create256Hash(decryptedDocument.toString()) - if (decryptedDocumentHash !== documentHash) { + if (documentHash && decryptedDocumentHash !== documentHash) { return { stream: null, status: {