From 070b4ba6c969915d9b96a123122cc52a66b245a8 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 10:50:03 +0200 Subject: [PATCH 01/30] refactor signature --- src/@types/commands.ts | 40 +++-- src/components/Auth/index.ts | 8 +- .../Indexer/processors/BaseProcessor.ts | 10 +- src/components/core/admin/adminHandler.ts | 92 +++++++++- src/components/core/admin/getLogsHandler.ts | 62 ++++--- src/components/core/compute/getResults.ts | 2 +- .../core/compute/getStreamableLogs.ts | 2 +- src/components/core/compute/startCompute.ts | 4 +- src/components/core/compute/stopCompute.ts | 2 +- src/components/core/handler/authHandler.ts | 4 +- src/components/core/handler/ddoHandler.ts | 2 +- .../core/handler/downloadHandler.ts | 2 +- src/components/core/handler/encryptHandler.ts | 4 +- src/components/core/handler/handler.ts | 4 +- src/components/core/utils/nonceHandler.ts | 9 +- src/components/database/sqlite.ts | 2 +- src/components/httpRoutes/adminConfig.ts | 8 +- src/components/httpRoutes/logs.ts | 71 +++----- src/test/integration/algorithmsAccess.test.ts | 13 +- src/test/integration/auth.test.ts | 31 +++- src/test/integration/compute.test.ts | 169 ++++++++---------- src/test/integration/configAdmin.test.ts | 140 +++++++++------ src/test/integration/credentials.test.ts | 37 ++-- src/test/integration/download.test.ts | 19 +- .../integration/encryptDecryptDDO.test.ts | 21 +-- src/test/integration/encryptFile.test.ts | 37 ++-- .../integration/operationsDashboard.test.ts | 111 ++++++++---- src/test/unit/commands.test.ts | 23 ++- src/test/unit/download.test.ts | 21 ++- src/test/unit/indexer/validation.test.ts | 10 +- src/test/utils/signature.ts | 28 +++ src/utils/auth.ts | 80 +-------- 32 files changed, 569 insertions(+), 499 deletions(-) create mode 100644 src/test/utils/signature.ts diff --git a/src/@types/commands.ts b/src/@types/commands.ts index 4a51ff2e6..3f2636ea0 100644 --- a/src/@types/commands.ts +++ b/src/@types/commands.ts @@ -35,10 +35,10 @@ export interface FindPeerCommand extends Command { export interface GetP2PPeersCommand extends Command {} export interface GetP2PNetworkStatsCommand extends Command {} -export interface AdminCommand extends Command { - expiryTimestamp: number +export interface SignedCommand extends Command { + nonce: string signature: string - address?: string + address: string } export interface AdminCollectFeesHandlerResponse { @@ -151,31 +151,32 @@ export interface GetFeesCommand extends Command { policyServer?: any // object to pass to policyServer } // admin commands -export interface AdminStopNodeCommand extends AdminCommand {} -export interface AdminReindexTxCommand extends AdminCommand { +export interface AdminStopNodeCommand extends SignedCommand {} +export interface AdminReindexTxCommand extends SignedCommand { chainId: number txId: string } -export interface AdminCollectFeesCommand extends AdminCommand { +export interface AdminCollectFeesCommand extends SignedCommand { tokenAddress: string chainId: number tokenAmount?: number destinationAddress: string } -export interface AdminReindexChainCommand extends AdminCommand { +export interface AdminReindexChainCommand extends SignedCommand { chainId: number block?: number } -export interface AdminFetchConfigCommand extends AdminCommand {} +export interface AdminFetchConfigCommand extends SignedCommand {} -export interface AdminPushConfigCommand extends AdminCommand { +export interface AdminPushConfigCommand extends SignedCommand { config: Record } -export interface AdminGetLogsCommand extends AdminCommand { +export interface AdminGetLogsCommand extends SignedCommand { + logId?: string startTime?: string endTime?: string maxLogs?: number @@ -183,6 +184,15 @@ export interface AdminGetLogsCommand extends AdminCommand { level?: string page?: number } +/* eslint-disable no-unused-vars */ +export enum IndexingCommand { + STOP_THREAD = 'STOP_THREAD', + START_THREAD = 'START_THREAD' +} +export interface StartStopIndexingCommand extends SignedCommand { + chainId?: number + action: IndexingCommand +} export interface ICommandHandler { handle(command: Command): Promise @@ -194,7 +204,7 @@ export interface IValidateCommandHandler extends ICommandHandler { } export interface IValidateAdminCommandHandler extends ICommandHandler { - validate(command: AdminCommand): Promise + validate(command: SignedCommand): Promise } export interface ComputeGetEnvironmentsCommand extends Command { @@ -285,14 +295,6 @@ export interface JobStatus { status: CommandStatus hash: string } -export enum IndexingCommand { - STOP_THREAD = 'start', - START_THREAD = 'stop' -} -export interface StartStopIndexingCommand extends AdminCommand { - chainId?: number - action: IndexingCommand -} export interface PolicyServerPassthroughCommand extends Command { policyServerPassthrough?: any diff --git a/src/components/Auth/index.ts b/src/components/Auth/index.ts index 879c52625..76e977afe 100644 --- a/src/components/Auth/index.ts +++ b/src/components/Auth/index.ts @@ -10,7 +10,7 @@ export interface AuthValidation { address?: string nonce?: string signature?: string - message?: string + command?: string chainId?: string | null } @@ -79,16 +79,16 @@ export class Auth { async validateAuthenticationOrToken( authValidation: AuthValidation ): Promise { - const { token, address, nonce, signature, message, chainId } = authValidation + const { token, address, nonce, signature, command, chainId } = authValidation try { if (signature && address && nonce) { const oceanNode = OceanNode.getInstance() const nonceCheckResult: NonceResponse = await checkNonce( oceanNode.getDatabase().nonce, address, - parseInt(nonce), + parseFloat(nonce), signature, - message, + command, chainId ) diff --git a/src/components/Indexer/processors/BaseProcessor.ts b/src/components/Indexer/processors/BaseProcessor.ts index caf4372ce..5bde49364 100644 --- a/src/components/Indexer/processors/BaseProcessor.ts +++ b/src/components/Indexer/processors/BaseProcessor.ts @@ -253,8 +253,6 @@ export abstract class BaseEventProcessor { const wallet = keyManager.getEthWallet() const ethAddress = wallet.address - const useTxIdOrContractAddress = txId || contractAddress - if (URLUtils.isValidUrl(decryptorURL)) { try { const response = await withRetrial(async () => { @@ -264,7 +262,7 @@ export abstract class BaseEventProcessor { ) const message = String( - useTxIdOrContractAddress + ethAddress + chainId.toString() + nonce + String(ethAddress) + String(nonce) + String(PROTOCOL_COMMANDS.DECRYPT_DDO) ) const signature = await keyManager.signMessage(message) @@ -373,7 +371,7 @@ export abstract class BaseEventProcessor { ) const message = String( - useTxIdOrContractAddress + ethAddress + chainId.toString() + nonceP2p + String(ethAddress) + String(nonceP2p) + String(PROTOCOL_COMMANDS.DECRYPT_DDO) ) const signature = await keyManager.signMessage(message) @@ -431,7 +429,9 @@ export abstract class BaseEventProcessor { ) const messageToSign = String( - useTxIdOrContractAddress + ethAddress + chainId.toString() + remoteNonce + String(ethAddress) + + String(remoteNonce) + + String(PROTOCOL_COMMANDS.DECRYPT_DDO) ) const signature = await keyManager.signMessage(messageToSign) diff --git a/src/components/core/admin/adminHandler.ts b/src/components/core/admin/adminHandler.ts index 406cd8c31..20bbdb825 100644 --- a/src/components/core/admin/adminHandler.ts +++ b/src/components/core/admin/adminHandler.ts @@ -1,4 +1,4 @@ -import { AdminCommand, IValidateAdminCommandHandler } from '../../../@types/commands.js' +import { SignedCommand, IValidateAdminCommandHandler } from '../../../@types/commands.js' import { ValidateParams, validateCommandParameters, @@ -6,17 +6,21 @@ import { buildRateLimitReachedResponse, buildInvalidParametersResponse } from '../../httpRoutes/validateCommands.js' -import { validateAdminSignature } from '../../../utils/auth.js' +import { getAdminAddresses } from '../../../utils/auth.js' +import { checkSingleCredential } from '../../../utils/credentials.js' +import { CREDENTIALS_TYPES } from '../../../@types/DDO/Credentials.js' import { BaseHandler } from '../handler/handler.js' import { P2PCommandResponse } from '../../../@types/OceanNode.js' import { ReadableString } from '../../P2P/handleProtocolCommands.js' import { CommonValidation } from '../../../utils/validators.js' +import { getConfiguration } from '../../../utils/index.js' +import { CORE_LOGGER } from '../../../utils/logging/common.js' export abstract class AdminCommandHandler extends BaseHandler implements IValidateAdminCommandHandler { - async verifyParamsAndRateLimits(task: AdminCommand): Promise { + async verifyParamsAndRateLimits(task: SignedCommand): Promise { if (!(await this.checkRateLimit(task.caller))) { return buildRateLimitReachedResponse() } @@ -33,7 +37,76 @@ export abstract class AdminCommandHandler } } - async validate(command: AdminCommand): Promise { + async validateTokenOrSignature( + address: string, + nonce: string, + signature: string, + command: string, + chainId?: string + ): Promise { + const oceanNode = this.getOceanNode() + const auth = oceanNode.getAuth() + if (!auth) { + return { + valid: false, + error: 'Auth not configured' + } + } + const isAuthRequestValid = await auth.validateAuthenticationOrToken({ + token: null, + address, + nonce, + signature, + command, + chainId + }) + if (!isAuthRequestValid.valid) { + return { + valid: false, + error: isAuthRequestValid.error + } + } + try { + const config = await getConfiguration() + const allowedAdmins = await getAdminAddresses(config) + + const { addresses, accessLists } = allowedAdmins + let allowed = await checkSingleCredential( + { type: CREDENTIALS_TYPES.ADDRESS, values: addresses }, + address, + null + ) + if (allowed) { + return { valid: true, error: '' } + } + if (accessLists) { + for (const chainId of Object.keys(accessLists)) { + allowed = await checkSingleCredential( + { + type: CREDENTIALS_TYPES.ACCESS_LIST, + chainId: parseInt(chainId), + accessList: accessLists[chainId] + }, + address, + null + ) + if (allowed) { + return { valid: true, error: '' } + } + } + } + + const errorMsg = `The address which signed the message is not on the allowed admins list. Therefore signature ${signature} is rejected` + CORE_LOGGER.logMessage(errorMsg) + return { valid: false, error: errorMsg } + } catch (e) { + const errorMsg = `Error during signature validation: ${e}` + CORE_LOGGER.error(errorMsg) + return { valid: false, error: errorMsg } + } + } + + async validate(command: SignedCommand): Promise { const commandValidation = validateCommandParameters(command, [ 'expiryTimestamp', 'signature' @@ -41,14 +114,15 @@ export abstract class AdminCommandHandler if (!commandValidation.valid) { return buildInvalidRequestMessage(commandValidation.reason) } - const signatureValidation: CommonValidation = await validateAdminSignature( - command.expiryTimestamp, + const isAuthRequestValid = await this.validateTokenOrSignature( + command.address, + command.nonce, command.signature, - command.address + command.command ) - if (!signatureValidation.valid) { + if (!isAuthRequestValid.valid) { return buildInvalidRequestMessage( - `Signature check failed: ${signatureValidation.error}` + `Signature check failed: ${isAuthRequestValid.error}` ) } return { valid: true } diff --git a/src/components/core/admin/getLogsHandler.ts b/src/components/core/admin/getLogsHandler.ts index 4705a9894..7cb7f8a4a 100644 --- a/src/components/core/admin/getLogsHandler.ts +++ b/src/components/core/admin/getLogsHandler.ts @@ -18,37 +18,51 @@ export class GetLogsHandler extends AdminCommandHandler { if (!validation.valid) { return buildInvalidParametersResponse(validation) } - try { - const startTime = task.startTime - ? new Date(task.startTime) - : new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Default to 7 days ago - const endTime = task.endTime ? new Date(task.endTime) : new Date() // Default to now - const maxLogs = Math.min(task.maxLogs ?? 100, 1000) - const { moduleName, level, page } = task + if (task.logId) { + const logs = await this.getOceanNode().getDatabase().logs.retrieveLog(task.logId) + if (logs) { + return { + status: { httpStatus: 200 }, + stream: new ReadableString(JSON.stringify(logs)) + } + } else { + return { + status: { httpStatus: 404 }, + stream: new ReadableString('Log not found') + } + } + } else { + const startTime = task.startTime + ? new Date(task.startTime) + : new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Default to 7 days ago + const endTime = task.endTime ? new Date(task.endTime) : new Date() // Default to now + const maxLogs = Math.min(task.maxLogs ?? 100, 1000) + const { moduleName, level, page } = task - const logs = await this.getOceanNode() - .getDatabase() - .logs.retrieveMultipleLogs(startTime, endTime, maxLogs, moduleName, level, page) + const logs = await this.getOceanNode() + .getDatabase() + .logs.retrieveMultipleLogs(startTime, endTime, maxLogs, moduleName, level, page) + + if (!logs || logs.length === 0) { + const fileLogs = await readExceptionLogFiles( + startTime, + endTime, + maxLogs, + moduleName, + level + ) + return { + status: { httpStatus: 200 }, + stream: new ReadableString(JSON.stringify(fileLogs)) + } + } - if (!logs || logs.length === 0) { - const fileLogs = await readExceptionLogFiles( - startTime, - endTime, - maxLogs, - moduleName, - level - ) return { status: { httpStatus: 200 }, - stream: new ReadableString(JSON.stringify(fileLogs)) + stream: new ReadableString(JSON.stringify(logs)) } } - - return { - status: { httpStatus: 200 }, - stream: new ReadableString(JSON.stringify(logs)) - } } catch (error) { return { status: { httpStatus: 500, error: `Error retrieving logs: ${error.message}` }, diff --git a/src/components/core/compute/getResults.ts b/src/components/core/compute/getResults.ts index 682278f07..888d6f61d 100644 --- a/src/components/core/compute/getResults.ts +++ b/src/components/core/compute/getResults.ts @@ -36,7 +36,7 @@ export class ComputeGetResultHandler extends CommandHandler { task.consumerAddress, task.nonce, task.signature, - String(task.consumerAddress + task.jobId + task.index.toString() + task.nonce) + task.command ) if (authValidationResponse.status.httpStatus !== 200) { return authValidationResponse diff --git a/src/components/core/compute/getStreamableLogs.ts b/src/components/core/compute/getStreamableLogs.ts index 45c701d28..17e6d7e39 100644 --- a/src/components/core/compute/getStreamableLogs.ts +++ b/src/components/core/compute/getStreamableLogs.ts @@ -36,7 +36,7 @@ export class ComputeGetStreamableLogsHandler extends CommandHandler { task.consumerAddress, task.nonce, task.signature, - String(task.consumerAddress + task.jobId + task.nonce) + task.command ) if (authValidationResponse.status.httpStatus !== 200) { return authValidationResponse diff --git a/src/components/core/compute/startCompute.ts b/src/components/core/compute/startCompute.ts index 48aa56f49..9d00b28d6 100644 --- a/src/components/core/compute/startCompute.ts +++ b/src/components/core/compute/startCompute.ts @@ -73,7 +73,7 @@ export class PaidComputeStartHandler extends CommandHandler { task.consumerAddress, task.nonce, task.signature, - String(task.consumerAddress + task.datasets[0]?.documentId + task.nonce) + task.command ) if (authValidationResponse.status.httpStatus !== 200) { @@ -663,7 +663,7 @@ export class FreeComputeStartHandler extends CommandHandler { task.consumerAddress, task.nonce, task.signature, - String(task.nonce) + task.command ) if (authValidationResponse.status.httpStatus !== 200) { return authValidationResponse diff --git a/src/components/core/compute/stopCompute.ts b/src/components/core/compute/stopCompute.ts index bed33cd9d..1280a8c9c 100644 --- a/src/components/core/compute/stopCompute.ts +++ b/src/components/core/compute/stopCompute.ts @@ -34,7 +34,7 @@ export class ComputeStopHandler extends CommandHandler { task.consumerAddress, task.nonce, task.signature, - String(task.consumerAddress + (task.jobId || '')) + task.command ) if (authValidationResponse.status.httpStatus !== 200) { return authValidationResponse diff --git a/src/components/core/handler/authHandler.ts b/src/components/core/handler/authHandler.ts index fbe5c45d2..1ec6923c8 100644 --- a/src/components/core/handler/authHandler.ts +++ b/src/components/core/handler/authHandler.ts @@ -44,7 +44,7 @@ export class CreateAuthTokenHandler extends CommandHandler { address, parseInt(nonce), signature, - String(address + nonce), + task.command, task.chainId ) @@ -96,7 +96,7 @@ export class InvalidateAuthTokenHandler extends CommandHandler { address, parseInt(nonce), signature, - String(address + nonce), + task.command, task.chainId ) if (!isValid) { diff --git a/src/components/core/handler/ddoHandler.ts b/src/components/core/handler/ddoHandler.ts index 3438fcfae..539f7fa7b 100644 --- a/src/components/core/handler/ddoHandler.ts +++ b/src/components/core/handler/ddoHandler.ts @@ -815,7 +815,7 @@ export class ValidateDDOHandler extends CommandHandler { task.publisherAddress, task.nonce, task.signature, - String(task.publisherAddress + task.nonce) + task.command ) if (validationResponse.status.httpStatus !== 200) { return validationResponse diff --git a/src/components/core/handler/downloadHandler.ts b/src/components/core/handler/downloadHandler.ts index 0fd5d6ade..1d95d1dce 100644 --- a/src/components/core/handler/downloadHandler.ts +++ b/src/components/core/handler/downloadHandler.ts @@ -191,7 +191,7 @@ export class DownloadHandler extends CommandHandler { task.consumerAddress, task.nonce, task.signature, - String(task.documentId + task.nonce) + task.command ) if (isAuthRequestValid.status.httpStatus !== 200) { return isAuthRequestValid diff --git a/src/components/core/handler/encryptHandler.ts b/src/components/core/handler/encryptHandler.ts index f2acf4d0b..520d92807 100644 --- a/src/components/core/handler/encryptHandler.ts +++ b/src/components/core/handler/encryptHandler.ts @@ -59,7 +59,7 @@ export class EncryptHandler extends CommandHandler { task.consumerAddress, task.nonce, task.signature, - String(task.nonce) + task.command ) if (isAuthRequestValid.status.httpStatus !== 200) { return isAuthRequestValid @@ -150,7 +150,7 @@ export class EncryptFileHandler extends CommandHandler { task.consumerAddress, task.nonce, task.signature, - String(task.nonce) + task.command ) if (isAuthRequestValid.status.httpStatus !== 200) { return isAuthRequestValid diff --git a/src/components/core/handler/handler.ts b/src/components/core/handler/handler.ts index 30487f5c6..12455bf92 100644 --- a/src/components/core/handler/handler.ts +++ b/src/components/core/handler/handler.ts @@ -178,7 +178,7 @@ export abstract class CommandHandler address: string, nonce: string, signature: string, - message: string + command: string ): Promise { const oceanNode = this.getOceanNode() const auth = oceanNode.getAuth() @@ -193,7 +193,7 @@ export abstract class CommandHandler address, nonce, signature, - message + command }) if (!isAuthRequestValid.valid) { return { diff --git a/src/components/core/utils/nonceHandler.ts b/src/components/core/utils/nonceHandler.ts index 9fdd86a98..487922254 100644 --- a/src/components/core/utils/nonceHandler.ts +++ b/src/components/core/utils/nonceHandler.ts @@ -120,7 +120,7 @@ export async function checkNonce( consumer: string, nonce: number, signature: string, - message: string, + command: string, chainId?: string | null ): Promise { try { @@ -136,7 +136,7 @@ export async function checkNonce( previousNonce, // will return 0 if none exists consumer, signature, - message, // String(ddoId + nonce) + command, chainId ) if (validate.valid) { @@ -184,7 +184,7 @@ async function validateNonceAndSignature( existingNonce: number, consumer: string, signature: string, - message: string = null, + command: string = null, chainId?: string | null ): Promise { if (nonce <= existingNonce) { @@ -193,8 +193,7 @@ async function validateNonceAndSignature( error: 'nonce: ' + nonce + ' is not a valid nonce' } } - - if (!message) message = String(nonce) + const message = String(String(consumer) + String(nonce) + String(command)) const consumerMessage = ethers.solidityPackedKeccak256( ['bytes'], [ethers.hexlify(ethers.toUtf8Bytes(message))] diff --git a/src/components/database/sqlite.ts b/src/components/database/sqlite.ts index 61231688e..7a51274a3 100644 --- a/src/components/database/sqlite.ts +++ b/src/components/database/sqlite.ts @@ -24,7 +24,7 @@ export class SQLiteProvider implements DatabaseProvider { const createTableSQL = ` CREATE TABLE IF NOT EXISTS ${this.schemaNonce.name} ( id TEXT PRIMARY KEY, - nonce INTEGER + nonce REAL ); ` return new Promise((resolve, reject) => { diff --git a/src/components/httpRoutes/adminConfig.ts b/src/components/httpRoutes/adminConfig.ts index 4a3c0d071..303c9215a 100644 --- a/src/components/httpRoutes/adminConfig.ts +++ b/src/components/httpRoutes/adminConfig.ts @@ -10,11 +10,11 @@ export const adminConfigRoutes = express.Router() adminConfigRoutes.get('/api/admin/config', express.json(), async (req, res) => { try { - const { expiryTimestamp, signature, address } = req.body + const { nonce, signature, address } = req.body const response = await new FetchConfigHandler(req.oceanNode).handle({ command: PROTOCOL_COMMANDS.FETCH_CONFIG, - expiryTimestamp, + nonce, signature, address, caller: req.caller @@ -35,11 +35,11 @@ adminConfigRoutes.get('/api/admin/config', express.json(), async (req, res) => { adminConfigRoutes.post('/api/admin/config/update', express.json(), async (req, res) => { try { - const { expiryTimestamp, signature, config, address } = req.body + const { nonce, signature, config, address } = req.body const response = await new PushConfigHandler(req.oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce, signature, config, address, diff --git a/src/components/httpRoutes/logs.ts b/src/components/httpRoutes/logs.ts index 8d82496b3..92bc34429 100644 --- a/src/components/httpRoutes/logs.ts +++ b/src/components/httpRoutes/logs.ts @@ -1,7 +1,5 @@ import express from 'express' -import { validateAdminSignature } from '../../utils/auth.js' import { HTTP_LOGGER } from '../../utils/logging/common.js' -import { CommonValidation } from '../../utils/validators.js' import { GetLogsHandler } from '../core/admin/getLogsHandler.js' import { PROTOCOL_COMMANDS } from '../../utils/constants.js' import { streamToObject } from '../../utils/util.js' @@ -9,43 +7,9 @@ import { Readable } from 'node:stream' export const logRoutes = express.Router() -// Middleware to validate signature and expiry timestamp -const validateRequest = async ( - req: express.Request, - res: express.Response, - next: express.NextFunction -) => { - const { signature, address } = req.body - let { expiryTimestamp } = req.body - - if (!signature) { - return res.status(400).send('Missing signature') - } - if (!expiryTimestamp) { - return res.status(400).send('Missing expiryTimestamp') - } - - // Ensure expiryTimestamp is a number - expiryTimestamp = Number(expiryTimestamp) - if (isNaN(expiryTimestamp)) { - return res.status(400).send('Invalid expiryTimestamp') - } - - const isValid: CommonValidation = await validateAdminSignature( - expiryTimestamp, - signature, - address - ) - if (!isValid.valid) { - return res.status(403).send(`Invalid signature: ${isValid.error}`) - } - - next() // Proceed to the next middleware/function if validation is successful -} - logRoutes.post('/logs', express.json(), async (req, res) => { try { - const { signature, expiryTimestamp, address } = req.body + const { signature, address, nonce } = req.body const maxLogs = Math.min( typeof req.query.maxLogs === 'string' ? parseInt(req.query.maxLogs, 10) : 100, @@ -61,7 +25,7 @@ logRoutes.post('/logs', express.json(), async (req, res) => { const response = await new GetLogsHandler(req.oceanNode).handle({ command: PROTOCOL_COMMANDS.GET_LOGS, signature, - expiryTimestamp, + nonce, address, startTime: req.query.startTime as string, endTime: req.query.endTime as string, @@ -84,22 +48,29 @@ logRoutes.post('/logs', express.json(), async (req, res) => { } }) -logRoutes.post('/log/:id', express.json(), validateRequest, async (req, res) => { +logRoutes.post('/log/:id', express.json(), async (req, res) => { try { - const logId = req.params.id - const database = req.oceanNode.getDatabase() - if (!database || !database.logs) { - res.status(503).send('Logs database is not available') - return + const { signature, nonce, address, logId } = req.body + if (!logId) { + res.status(400).send('id is missing') } - const log = await database.logs.retrieveLog(logId) - if (log) { - res.json(log) + const response = await new GetLogsHandler(req.oceanNode).handle({ + command: PROTOCOL_COMMANDS.GET_LOGS, + signature, + nonce, + address, + logId + }) + + if (response.status.httpStatus === 200) { + const result = await streamToObject(response.stream as Readable) + res.status(200).json(result) } else { - res.status(404).send('Log not found') + HTTP_LOGGER.log('LEVEL_ERROR', `Error fetching logs: ${response.status.error}`) + res.status(response.status.httpStatus).json({ error: response.status.error }) } } catch (error) { - HTTP_LOGGER.error(`Error retrieving log: ${error.message}`) - res.status(500).send('Internal Server Error' + error.message) + HTTP_LOGGER.error(`Error retrieving logs: ${error.message}`) + res.status(500).send(`Internal Server Error: ${error.message}`) } }) diff --git a/src/test/integration/algorithmsAccess.test.ts b/src/test/integration/algorithmsAccess.test.ts index 12cc2e114..8fbf6165f 100644 --- a/src/test/integration/algorithmsAccess.test.ts +++ b/src/test/integration/algorithmsAccess.test.ts @@ -48,6 +48,7 @@ import OceanToken from '@oceanprotocol/contracts/artifacts/contracts/utils/Ocean import EscrowJson from '@oceanprotocol/contracts/artifacts/contracts/escrow/Escrow.sol/Escrow.json' with { type: 'json' } import { createHash } from 'crypto' import { getAlgoChecksums } from '../../components/core/compute/utils.js' +import { createHashForSignature } from '../utils/signature.js' describe('Trusted algorithms Flow', () => { let previousConfiguration: OverrideEnvConfig[] @@ -432,14 +433,12 @@ describe('Trusted algorithms Flow', () => { } } const nonce = Date.now().toString() - const message = String( - (await consumerAccount.getAddress()) + publishedComputeDataset.ddo.id + nonce - ) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.COMPUTE_START ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const startComputeTask: PaidComputeStartCommand = { command: PROTOCOL_COMMANDS.COMPUTE_START, diff --git a/src/test/integration/auth.test.ts b/src/test/integration/auth.test.ts index 2a0672032..eb1da9c24 100644 --- a/src/test/integration/auth.test.ts +++ b/src/test/integration/auth.test.ts @@ -1,6 +1,6 @@ import { JsonRpcProvider, Signer, Wallet } from 'ethers' import { Database } from '../../components/database/index.js' -import { getConfiguration, getMessageHash } from '../../utils/index.js' +import { getConfiguration } from '../../utils/index.js' import { DEFAULT_TEST_TIMEOUT, OverrideEnvConfig, @@ -22,6 +22,7 @@ import { streamToObject } from '../../utils/util.js' import { Readable } from 'stream' import { expect } from 'chai' import { ValidateDDOHandler } from '../../components/core/handler/ddoHandler.js' +import { createHashForSignature } from '../utils/signature.js' describe('Auth Token Integration Tests', () => { let config: OceanNodeConfig @@ -116,8 +117,11 @@ describe('Auth Token Integration Tests', () => { const consumerAddress = await consumerAccount.getAddress() const nonce = getRandomNonce() - const message = String(consumerAddress + nonce) - const messageHash = getMessageHash(message) + const messageHash = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN + ) const signature = await consumerAccount.signMessage(messageHash) const handlerResponse = await new CreateAuthTokenHandler(oceanNode).handle({ @@ -137,8 +141,11 @@ describe('Auth Token Integration Tests', () => { const consumerAddress = await consumerAccount.getAddress() const nonce = getRandomNonce() - const message = String(consumerAddress + nonce) - const messageHash = getMessageHash(message) + const messageHash = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN + ) const signature = await consumerAccount.signMessage(messageHash) const validUntil = Date.now() + 1000 @@ -163,8 +170,11 @@ describe('Auth Token Integration Tests', () => { const consumerAddress = await consumerAccount.getAddress() const nonce = getRandomNonce() - const message = String(consumerAddress + nonce) - const messageHash = getMessageHash(message) + const messageHash = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN + ) const signature = await consumerAccount.signMessage(messageHash) const handlerResponse = await new CreateAuthTokenHandler(oceanNode).handle({ @@ -225,8 +235,11 @@ describe('Auth Token Integration Tests', () => { // Missing address const nonce = getRandomNonce() - const message = String((await consumerAccount.getAddress()) + nonce) - const messageHash = getMessageHash(message) + const messageHash = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN + ) const signature = await consumerAccount.signMessage(messageHash) const response2 = await new CreateAuthTokenHandler(oceanNode).handle({ diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index 2dbe382c8..fbf1d899a 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -80,6 +80,7 @@ import { freeComputeStartPayload } from '../data/commands.js' import { DDOManager } from '@oceanprotocol/ddo-js' import Dockerode from 'dockerode' import { C2DEngineDocker } from '../../components/c2d/compute_engine_docker.js' +import { createHashForSignature } from '../utils/signature.js' const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) @@ -606,16 +607,11 @@ describe('Compute', () => { it('should fail to start a compute job', async () => { const nonce = Date.now().toString() - const message = String( - (await consumerAccount.getAddress()) + publishedComputeDataset.ddo.id + nonce - ) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_START ) - const messageHashBytes = ethers.toBeArray(consumerMessage) - // since ganache does not supports personal_sign, we use wallet account const signature = await wallet.signMessage(messageHashBytes) const startComputeTask: PaidComputeStartCommand = { @@ -709,15 +705,11 @@ describe('Compute', () => { } const locksBefore = locks.length const nonce = Date.now().toString() - const message = String( - (await consumerAccount.getAddress()) + publishedComputeDataset.ddo.id + nonce - ) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_START ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const re = [] for (const res of firstEnv.resources) { @@ -801,14 +793,11 @@ describe('Compute', () => { ' Should have maxLockCounts in auth' ) const nonce2 = Date.now().toString() - const message2 = String( - (await consumerAccount.getAddress()) + publishedComputeDataset.ddo.id + nonce2 - ) - const consumerMessage2 = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message2))] + const messageHashBytes2 = createHashForSignature( + await consumerAccount.getAddress(), + nonce2, + PROTOCOL_COMMANDS.COMPUTE_START ) - const messageHashBytes2 = ethers.toBeArray(consumerMessage2) const signature2 = await wallet.signMessage(messageHashBytes2) response = await new PaidComputeStartHandler(oceanNode).handle({ ...startComputeTask, @@ -862,15 +851,11 @@ describe('Compute', () => { computeEnvironments = await streamToObject(eresponse.stream as Readable) console.log(computeEnvironments[0]) const nonce = Date.now().toString() - const message = String( - (await consumerAccount.getAddress()) + publishedComputeDataset.ddo.id + nonce - ) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_START ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const re = [] for (const res of firstEnv.resources) { @@ -914,13 +899,11 @@ describe('Compute', () => { it('should start a queued free docker compute job', async () => { const nonce = Date.now().toString() - const message = String(nonce) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_START ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const startComputeTask: FreeComputeStartCommand = { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, @@ -1001,13 +984,11 @@ describe('Compute', () => { }) it('should get job result by consumer', async () => { const nonce = Date.now().toString() - const message = String((await wallet.getAddress()) + jobId + '0' + nonce) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_GET_RESULT ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const resultComputeTask: ComputeGetResultCommand = { command: PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, @@ -1025,13 +1006,11 @@ describe('Compute', () => { }) it('should get job result by additional viewer', async () => { const nonce = Date.now().toString() - const message = String((await wallet2.getAddress()) + jobId + '0' + nonce) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_GET_RESULT ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet2.signMessage(messageHashBytes) const resultComputeTask: ComputeGetResultCommand = { command: PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, @@ -1049,13 +1028,11 @@ describe('Compute', () => { }) it('should fail to get job result by non allowed address', async () => { const nonce = Date.now().toString() - const message = String((await wallet3.getAddress()) + jobId + '0' + nonce) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await wallet3.address, + nonce, + PROTOCOL_COMMANDS.COMPUTE_GET_RESULT ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet3.signMessage(messageHashBytes) const resultComputeTask: ComputeGetResultCommand = { command: PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, @@ -1076,13 +1053,11 @@ describe('Compute', () => { it('should stop a compute job', async () => { const nonce = Date.now().toString() - const message = String((await consumerAccount.getAddress()) + (jobId || '')) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_STOP ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const stopComputeTask: ComputeStopCommand = { command: PROTOCOL_COMMANDS.COMPUTE_STOP, @@ -1130,14 +1105,13 @@ describe('Compute', () => { }) it('should deny the Free job due to bad container image (directCommand payload)', async function () { const nonce = Date.now().toString() - const message = String(nonce) - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await wallet.address, + nonce, + PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) + freeComputeStartPayload.command = PROTOCOL_COMMANDS.FREE_COMPUTE_START freeComputeStartPayload.signature = signature freeComputeStartPayload.nonce = nonce freeComputeStartPayload.environment = firstEnv.id @@ -1608,14 +1582,11 @@ describe('Compute', () => { const encryptedAuth = await encryptDockerRegistryAuth(validAuth) const nonce = Date.now().toString() - const message = String( - (await consumerAccount.getAddress()) + publishedComputeDataset.ddo.id + nonce - ) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_START ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const startComputeTask: PaidComputeStartCommand = { @@ -1670,14 +1641,11 @@ describe('Compute', () => { const encryptedAuth = await encryptDockerRegistryAuth(invalidAuth) const nonce = Date.now().toString() - const message = String( - (await consumerAccount.getAddress()) + publishedComputeDataset.ddo.id + nonce - ) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.COMPUTE_START ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const startComputeTask: PaidComputeStartCommand = { @@ -1726,11 +1694,12 @@ describe('Compute', () => { const encryptedAuth = await encryptDockerRegistryAuth(validAuth) const nonce = Date.now().toString() - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(nonce))] + const messageHashBytes = createHashForSignature( + await wallet.address, + nonce, + PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const signature = await wallet.signMessage(ethers.toBeArray(consumerMessage)) + const signature = await wallet.signMessage(messageHashBytes) const startComputeTask: FreeComputeStartCommand = { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, @@ -1782,11 +1751,12 @@ describe('Compute', () => { const encryptedAuth = await encryptDockerRegistryAuth(invalidAuth) const nonce = Date.now().toString() - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(nonce))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const signature = await wallet.signMessage(ethers.toBeArray(consumerMessage)) + const signature = await wallet.signMessage(messageHashBytes) const startComputeTask: FreeComputeStartCommand = { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, @@ -2061,12 +2031,12 @@ describe('Compute Access Restrictions', () => { envId: string ): Promise { const nonce = Date.now().toString() - const message = String(consumerAddr + publishedComputeDataset.ddo.id + nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + consumerAddr, + nonce, + PROTOCOL_COMMANDS.COMPUTE_START ) - const signature = await signerWallet.signMessage(ethers.toBeArray(consumerMessage)) + const signature = await signerWallet.signMessage(messageHashBytes) return { command: PROTOCOL_COMMANDS.COMPUTE_START, @@ -2098,11 +2068,12 @@ describe('Compute Access Restrictions', () => { envId: string ): Promise { const nonce = Date.now().toString() - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(nonce))] + const messageHashBytes = createHashForSignature( + consumerAddr, + nonce, + PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const signature = await signerWallet.signMessage(ethers.toBeArray(consumerMessage)) + const signature = await signerWallet.signMessage(messageHashBytes) return { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, diff --git a/src/test/integration/configAdmin.test.ts b/src/test/integration/configAdmin.test.ts index e971d5508..1cadda271 100644 --- a/src/test/integration/configAdmin.test.ts +++ b/src/test/integration/configAdmin.test.ts @@ -19,6 +19,7 @@ import { PushConfigHandler } from '../../components/core/admin/pushConfigHandler import { streamToObject } from '../../utils/util.js' import { Readable } from 'stream' import { expect } from 'chai' +import { createHashForSignature } from '../utils/signature.js' describe('Config Admin Endpoints Integration Tests', () => { let config: OceanNodeConfig @@ -60,21 +61,30 @@ describe('Config Admin Endpoints Integration Tests', () => { await tearDownEnvironment(previousConfiguration) }) - const getAdminSignature = async (expiryTimestamp: number): Promise => { - const message = expiryTimestamp.toString() - return await adminAccount.signMessage(message) + const getAdminSignature = async (nonce: string, command: string): Promise => { + // const message = expiryTimestamp.toString() + // return await adminAccount.signMessage(message) + + const messageHashBytes = createHashForSignature( + await adminAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.STOP_NODE + ) + const signature = await adminAccount.signMessage(messageHashBytes) + return signature } describe('Fetch Config Tests', () => { it('should fetch current config', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + const nonce = Date.now().toString() + const signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.FETCH_CONFIG) const handlerResponse = await new FetchConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.FETCH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature }) @@ -89,12 +99,13 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should hide private key in fetched config', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + const nonce = Date.now().toString() + const signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.FETCH_CONFIG) const handlerResponse = await new FetchConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.FETCH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature }) @@ -119,7 +130,8 @@ describe('Config Admin Endpoints Integration Tests', () => { const handlerResponse = await new FetchConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.FETCH_CONFIG, - expiryTimestamp, + nonce: expiryTimestamp.toString(), + address: await adminAccount.getAddress(), signature: invalidSignature }) @@ -129,13 +141,14 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should reject fetch config with expired timestamp', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() - 60000 - const signature = await getAdminSignature(expiryTimestamp) + const nonce = String(Date.now() - 60000) + const signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.FETCH_CONFIG) const handlerResponse = await new FetchConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.FETCH_CONFIG, - expiryTimestamp, - signature + nonce, + signature, + address: await adminAccount.getAddress() }) expect(handlerResponse.status.httpStatus).to.not.equal(200) @@ -146,8 +159,8 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should push config changes and reload node', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + const nonce = Date.now().toString() + const signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) const newConfig = { rateLimit: 100, @@ -156,8 +169,9 @@ describe('Config Admin Endpoints Integration Tests', () => { const handlerResponse = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce, signature, + address: await adminAccount.getAddress(), config: newConfig }) @@ -176,11 +190,13 @@ describe('Config Admin Endpoints Integration Tests', () => { rateLimit: 30, maxConnections: 30 } - + const nonce2 = Date.now().toString() + const signature2 = await getAdminSignature(nonce2, PROTOCOL_COMMANDS.PUSH_CONFIG) await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp: Date.now() + 60000, - signature: await getAdminSignature(Date.now() + 60000), + nonce, + signature: signature2, + address: await adminAccount.getAddress(), config: restoreConfig }) }) @@ -188,12 +204,13 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should merge new config with existing config', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + let nonce = Date.now().toString() + let signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.FETCH_CONFIG) const fetchResponse = await new FetchConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.FETCH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature }) @@ -202,11 +219,14 @@ describe('Config Admin Endpoints Integration Tests', () => { const partialConfig = { rateLimit: 75 } + nonce = Date.now().toString() + signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) const pushResponse = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp: Date.now() + 60000, - signature: await getAdminSignature(Date.now() + 60000), + nonce, + address: await adminAccount.getAddress(), + signature, config: partialConfig }) @@ -214,11 +234,14 @@ describe('Config Admin Endpoints Integration Tests', () => { expect(updatedConfig.rateLimit).to.equal(75) expect(updatedConfig.maxConnections).to.equal(currentConfig.maxConnections) + nonce = Date.now().toString() + signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp: Date.now() + 60000, - signature: await getAdminSignature(Date.now() + 60000), + nonce, + address: await adminAccount.getAddress(), + signature, config: { rateLimit: currentConfig.rateLimit } }) }) @@ -226,12 +249,13 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should hide private key in push config response', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + let nonce = Date.now().toString() + let signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) const response = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature, config: { rateLimit: 50 } }) @@ -242,11 +266,13 @@ describe('Config Admin Endpoints Integration Tests', () => { expect(updatedConfig).to.have.property('keys') expect(updatedConfig.keys).to.have.property('privateKey') expect(updatedConfig.keys.privateKey).to.equal('[*** HIDDEN CONTENT ***]') - + nonce = Date.now().toString() + signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp: Date.now() + 60000, - signature: await getAdminSignature(Date.now() + 60000), + nonce, + address: await adminAccount.getAddress(), + signature, config: { rateLimit: 30 } }) }) @@ -264,7 +290,8 @@ describe('Config Admin Endpoints Integration Tests', () => { const handlerResponse = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce: expiryTimestamp.toString(), + address: await adminAccount.getAddress(), signature: invalidSignature, config: { rateLimit: 100 } }) @@ -272,15 +299,16 @@ describe('Config Admin Endpoints Integration Tests', () => { expect(handlerResponse.status.httpStatus).to.not.equal(200) }) - it('should reject push config with expired timestamp', async function () { + it('should reject push config with old nonce', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) const expiryTimestamp = Date.now() - 60000 - const signature = await getAdminSignature(expiryTimestamp) - + const nonce = expiryTimestamp.toString() + const signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) const handlerResponse = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature, config: { rateLimit: 100 } }) @@ -291,12 +319,13 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should reject push config with missing config parameter', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + const nonce = Date.now().toString() + const signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) const handlerResponse = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature, config: undefined }) @@ -307,12 +336,13 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should reject push config with invalid config type', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + const nonce = Date.now().toString() + const signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) const handlerResponse = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature, config: 'invalid' as any }) @@ -323,12 +353,13 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should reject push config with invalid field values (Zod validation)', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + const nonce = Date.now().toString() + const signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) const handlerResponse = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature, config: { rateLimit: 'not-a-number' as any } }) @@ -343,14 +374,15 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should reload node configuration after push', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) + let nonce = Date.now().toString() + let signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) const configBefore = await getConfiguration() await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, + nonce, + address: await adminAccount.getAddress(), signature, config: { rateLimit: 999 } }) @@ -359,11 +391,13 @@ describe('Config Admin Endpoints Integration Tests', () => { expect(configAfter.rateLimit).to.equal(999) expect(configAfter.rateLimit).to.not.equal(configBefore.rateLimit) - + nonce = Date.now().toString() + signature = await getAdminSignature(nonce, PROTOCOL_COMMANDS.PUSH_CONFIG) await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp: Date.now() + 60000, - signature: await getAdminSignature(Date.now() + 60000), + nonce, + address: await adminAccount.getAddress(), + signature, config: { rateLimit: configBefore.rateLimit } }) }) diff --git a/src/test/integration/credentials.test.ts b/src/test/integration/credentials.test.ts index d2bd88d56..d2ecb0243 100644 --- a/src/test/integration/credentials.test.ts +++ b/src/test/integration/credentials.test.ts @@ -66,6 +66,7 @@ import { ComputeInitializeHandler } from '../../components/core/compute/initiali import { ComputeAlgorithm, ComputeAsset } from '../../@types/index.js' import { ComputeGetEnvironmentsHandler } from '../../components/core/compute/environments.js' import { ComputeInitializeCommand } from '../../@types/commands.js' +import { createHashForSignature } from '../utils/signature.js' describe('[Credentials Flow] - Should run a complete node flow.', () => { let config: OceanNodeConfig @@ -337,12 +338,11 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { const wallet = new ethers.Wallet(consumerPrivateKey) const nonce = Date.now().toString() - const message = String(ddoWithMatchAll.id + nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.DOWNLOAD ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const downloadTask = { @@ -376,12 +376,11 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { const wallet = new ethers.Wallet(consumerPrivateKey) const nonce = Date.now().toString() - const message = String(ddo.id + nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.DOWNLOAD ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const downloadTask = { @@ -527,12 +526,11 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { const wallet = new ethers.Wallet(consumerPrivateKey) const nonce = Date.now().toString() - const message = String(ddo.id + nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.DOWNLOAD ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const downloadTask = { @@ -568,12 +566,11 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { const wallet = new ethers.Wallet(consumerPrivateKey) const nonce = Date.now().toString() - const message = String(ddo.id + nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.DOWNLOAD ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const downloadTask = { diff --git a/src/test/integration/download.test.ts b/src/test/integration/download.test.ts index 1b5194950..861824a9c 100644 --- a/src/test/integration/download.test.ts +++ b/src/test/integration/download.test.ts @@ -43,6 +43,7 @@ import { publishAsset, orderAsset } from '../utils/assets.js' import { downloadAsset } from '../data/assets.js' import { genericDDO } from '../data/ddo.js' import { homedir } from 'os' +import { createHashForSignature } from '../utils/signature.js' describe('[Download Flow] - Should run a complete node flow.', () => { let config: OceanNodeConfig @@ -247,12 +248,11 @@ describe('[Download Flow] - Should run a complete node flow.', () => { '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' ) const nonce = Date.now().toString() - const message = String(publishedDataset.ddo.id + nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.DOWNLOAD ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const downloadTask = { fileIndex: 0, @@ -294,12 +294,11 @@ describe('[Download Flow] - Should run a complete node flow.', () => { it('should not allow to download the asset with different consumer address', async function () { const assetDID = publishedDataset.ddo.id const nonce = Date.now().toString() - const message = String(assetDID + nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await anotherConsumer.getAddress(), + nonce, + PROTOCOL_COMMANDS.DOWNLOAD ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await anotherConsumer.signMessage(messageHashBytes) const doCheck = async () => { diff --git a/src/test/integration/encryptDecryptDDO.test.ts b/src/test/integration/encryptDecryptDDO.test.ts index cfaffb163..2cdb2fbe7 100644 --- a/src/test/integration/encryptDecryptDDO.test.ts +++ b/src/test/integration/encryptDecryptDDO.test.ts @@ -39,6 +39,7 @@ import { DecryptDDOCommand } from '../../@types/commands.js' import { EncryptMethod } from '../../@types/fileObject.js' import { homedir } from 'os' import { OceanIndexer } from '../../components/Indexer/index.js' +import { createHashForSignature } from '../utils/signature.js' describe('Should encrypt and decrypt DDO', () => { let database: Database @@ -339,12 +340,12 @@ describe('Should encrypt and decrypt DDO', () => { it('should decrypt ddo with transactionId and return it', async () => { const nonce = Date.now().toString() const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) - const message = String(txReceiptEncryptDDO.hash + publisherAddress + chainId + nonce) - const messageHash = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.DECRYPT_DDO ) - const signature = await wallet.signMessage(messageHash) + const signature = await wallet.signMessage(messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, @@ -365,12 +366,12 @@ describe('Should encrypt and decrypt DDO', () => { it('should decrypt ddo with encryptedDocument, flags, documentHash and return it', async () => { const nonce = Date.now().toString() const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) - const message = String(dataNftAddress + publisherAddress + chainId + nonce) - const messageHash = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.DECRYPT_DDO ) - const signature = await wallet.signMessage(messageHash) + const signature = await wallet.signMessage(messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, diff --git a/src/test/integration/encryptFile.test.ts b/src/test/integration/encryptFile.test.ts index aa55e3ab5..685c162b1 100644 --- a/src/test/integration/encryptFile.test.ts +++ b/src/test/integration/encryptFile.test.ts @@ -17,6 +17,7 @@ import { tearDownEnvironment } from '../utils/utils.js' import { Database } from '../../components/database/index.js' +import { createHashForSignature } from '../utils/signature.js' describe('Encrypt File', () => { let config: OceanNodeConfig @@ -45,12 +46,11 @@ describe('Encrypt File', () => { '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' ) const nonce = Date.now().toString() - const message = String(nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.ENCRYPT_FILE ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const encryptFileTask: EncryptFileCommand = { command: PROTOCOL_COMMANDS.ENCRYPT_FILE, @@ -83,12 +83,11 @@ describe('Encrypt File', () => { // should return a buffer const file: Buffer = fs.readFileSync('src/test/data/organizations-100.aes') const nonce = Date.now().toString() - const message = String(nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await anotherConsumerWallet.getAddress(), + nonce, + PROTOCOL_COMMANDS.ENCRYPT_FILE ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await anotherConsumerWallet.signMessage(messageHashBytes) const encryptFileTask: EncryptFileCommand = { command: PROTOCOL_COMMANDS.ENCRYPT_FILE, @@ -116,12 +115,11 @@ describe('Encrypt File', () => { // should return a buffer const file: Buffer = fs.readFileSync('src/test/data/organizations-100.aes') const nonce = Date.now().toString() - const message = String(nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await anotherConsumerWallet.getAddress(), + nonce, + PROTOCOL_COMMANDS.ENCRYPT_FILE ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await anotherConsumerWallet.signMessage(messageHashBytes) const encryptFileTask: EncryptFileCommand = { command: PROTOCOL_COMMANDS.ENCRYPT_FILE, @@ -147,12 +145,11 @@ describe('Encrypt File', () => { it('should return unknown file type', async () => { const nonce = Date.now().toString() - const message = String(nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await anotherConsumerWallet.getAddress(), + nonce, + PROTOCOL_COMMANDS.ENCRYPT_FILE ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await anotherConsumerWallet.signMessage(messageHashBytes) const encryptFileTask: EncryptFileCommand = { command: PROTOCOL_COMMANDS.ENCRYPT_FILE, diff --git a/src/test/integration/operationsDashboard.test.ts b/src/test/integration/operationsDashboard.test.ts index 8f102b741..8360d9502 100644 --- a/src/test/integration/operationsDashboard.test.ts +++ b/src/test/integration/operationsDashboard.test.ts @@ -55,6 +55,7 @@ import { CollectFeesHandler } from '../../components/core/admin/collectFeesHandl import { getProviderFeeToken } from '../../components/core/utils/feesHandler.js' import { KeyManager } from '../../components/KeyManager/index.js' import { BlockchainRegistry } from '../../components/BlockchainRegistry/index.js' +import { createHashForSignature } from '../utils/signature.js' describe('Should test admin operations', () => { let config: OceanNodeConfig @@ -62,12 +63,6 @@ describe('Should test admin operations', () => { let publishedDataset: any let dbconn: Database let indexer: OceanIndexer - const currentDate = new Date() - const expiryTimestamp = new Date( - currentDate.getFullYear() + 1, - currentDate.getMonth(), - currentDate.getDate() - ).getTime() const provider = new JsonRpcProvider('http://127.0.0.1:8545') const wallet = new ethers.Wallet( '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58', @@ -127,17 +122,19 @@ describe('Should test admin operations', () => { oceanNode.addIndexer(indexer) }) - async function getSignature(message: string) { - return await wallet.signMessage(message) - } - it('validation should pass for stop node command', async () => { - const signature = await getSignature(expiryTimestamp.toString()) - + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.STOP_NODE + ) + const signature = await wallet.signMessage(messageHashBytes) const stopNodeCommand: AdminStopNodeCommand = { command: PROTOCOL_COMMANDS.STOP_NODE, node: oceanNode.getKeyManager().getPeerId().toString(), - expiryTimestamp, + nonce, + address: wallet.address, signature } const validationResponse = await new StopNodeHandler(oceanNode).validate( @@ -155,14 +152,21 @@ describe('Should test admin operations', () => { oceanNode ).getHandler(PROTOCOL_COMMANDS.COLLECT_FEES) as CollectFeesHandler - const signature = await getSignature(expiryTimestamp.toString()) + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.COLLECT_FEES + ) + const signature = await wallet.signMessage(messageHashBytes) const collectFeesCommand: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, tokenAddress: await getProviderFeeToken(DEVELOPMENT_CHAIN_ID), chainId: DEVELOPMENT_CHAIN_ID, tokenAmount: 0.01, destinationAddress: await destinationWallet.getAddress(), - expiryTimestamp, + address: wallet.address, + nonce, signature } const validationResponse = await collectFeesHandler.validate(collectFeesCommand) @@ -191,8 +195,15 @@ describe('Should test admin operations', () => { expect(await token.balanceOf(await destinationWallet.getAddress())).to.be.equal( balanceBefore + parseUnits(collectFeesCommand.tokenAmount.toString(), 'ether') ) - + await sleep(1000) // Test incorrect values for command: node ID and big amount + const nonce2 = Date.now().toString() + const messageHashBytes2 = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.COLLECT_FEES + ) + const signature2 = await wallet.signMessage(messageHashBytes2) const collectFeesCommandWrongNode: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, node: 'My peerID', // dummy peer ID @@ -200,21 +211,30 @@ describe('Should test admin operations', () => { chainId: DEVELOPMENT_CHAIN_ID, tokenAmount: 0.01, destinationAddress: await wallet.getAddress(), - expiryTimestamp, - signature + address: wallet.address, + nonce: nonce2, + signature: signature2 } expect( (await collectFeesHandler.handle(collectFeesCommandWrongNode)).status.httpStatus ).to.be.equal(400) // NOK - + await sleep(1000) + const nonce3 = Date.now().toString() + const messageHashBytes3 = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.COLLECT_FEES + ) + const signature3 = await wallet.signMessage(messageHashBytes3) const collectFeesCommandWrongAmount: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, tokenAddress: getOceanArtifactsAdresses().development.Ocean, chainId: DEVELOPMENT_CHAIN_ID, tokenAmount: 366666666666, // big amount destinationAddress: await wallet.getAddress(), - expiryTimestamp, - signature + address: wallet.address, + nonce: nonce3, + signature: signature3 } expect( (await collectFeesHandler.handle(collectFeesCommandWrongAmount)).status.httpStatus @@ -237,14 +257,21 @@ describe('Should test admin operations', () => { it('should pass for reindex tx command', async function () { this.timeout(DEFAULT_TEST_TIMEOUT * 2) await waitToIndex(publishedDataset.ddo.did, EVENTS.METADATA_CREATED) - const signature = await getSignature(expiryTimestamp.toString()) + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.REINDEX_TX + ) + const signature = await wallet.signMessage(messageHashBytes) const reindexTxCommand: AdminReindexTxCommand = { command: PROTOCOL_COMMANDS.REINDEX_TX, node: oceanNode.getKeyManager().getPeerId().toString(), txId: publishedDataset.trxReceipt.hash, chainId: DEVELOPMENT_CHAIN_ID, - expiryTimestamp, + address: wallet.address, + nonce, signature } const reindexTxHandler = new ReindexTxHandler(oceanNode) @@ -304,7 +331,6 @@ describe('Should test admin operations', () => { it('should pass for reindex chain command', async function () { this.timeout(DEFAULT_TEST_TIMEOUT * 2) - const signature = await getSignature(expiryTimestamp.toString()) this.timeout(DEFAULT_TEST_TIMEOUT * 2) const { ddo, wasTimeout } = await waitToIndex( publishedDataset.ddo.did, @@ -314,11 +340,19 @@ describe('Should test admin operations', () => { if (!ddo) { expect(expectedTimeoutFailure(this.test.title)).to.be.equal(wasTimeout) } else { + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.REINDEX_CHAIN + ) + const signature = await wallet.signMessage(messageHashBytes) const reindexChainCommand: AdminReindexChainCommand = { command: PROTOCOL_COMMANDS.REINDEX_CHAIN, node: oceanNode.getKeyManager().getPeerId().toString(), chainId: DEVELOPMENT_CHAIN_ID, - expiryTimestamp, + address: wallet.address, + nonce, signature } const reindexChainHandler = new ReindexChainHandler(oceanNode) @@ -377,26 +411,43 @@ describe('Should test admin operations', () => { true ).getHandler(PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD) as IndexingThreadHandler - const signature = await getSignature(expiryTimestamp.toString()) + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD + ) + const signature = await wallet.signMessage(messageHashBytes) + const indexingStartCommand: StartStopIndexingCommand = { command: PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD, action: IndexingCommand.START_THREAD, - expiryTimestamp, + address: wallet.address, + nonce, signature } expect((await indexingHandler.validate(indexingStartCommand)).valid).to.be.equal(true) // OK - + await sleep(1000) const indexingStopCommand: StartStopIndexingCommand = { command: PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD, action: IndexingCommand.STOP_THREAD, - expiryTimestamp: 10, + address: wallet.address, + nonce, signature } expect((await indexingHandler.validate(indexingStopCommand)).valid).to.be.equal(false) // NOK // OK now - indexingStopCommand.expiryTimestamp = expiryTimestamp indexingStopCommand.chainId = 8996 + const nonce2 = Date.now().toString() + const messageHashBytes2 = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD + ) + const signature2 = await wallet.signMessage(messageHashBytes2) + indexingStopCommand.signature = signature2 + indexingStopCommand.nonce = nonce2 expect((await indexingHandler.validate(indexingStopCommand)).valid).to.be.equal(true) // OK // should exist a running thread for this network atm diff --git a/src/test/unit/commands.test.ts b/src/test/unit/commands.test.ts index 9b358f2b8..1c2298b90 100644 --- a/src/test/unit/commands.test.ts +++ b/src/test/unit/commands.test.ts @@ -53,7 +53,8 @@ import { ReindexTxHandler } from '../../components/core/admin/reindexTxHandler.j import { ReindexChainHandler } from '../../components/core/admin/reindexChainHandler.js' import { CollectFeesHandler } from '../../components/core/admin/collectFeesHandler.js' import { GetJobsHandler } from '../../components/core/handler/getJobs.js' -import { Wallet, ethers } from 'ethers' +import { Wallet } from 'ethers' +import { createHashForSignature } from '../utils/signature.js' describe('Commands and handlers', () => { let node: OceanNode let consumerAccount: Wallet @@ -138,12 +139,11 @@ describe('Commands and handlers', () => { node ).getHandler(PROTOCOL_COMMANDS.ENCRYPT) let nonce = Date.now().toString() - let message = String(nonce) - let consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.ENCRYPT ) - let messageHashBytes = ethers.toBeArray(consumerMessage) let signature = await consumerAccount.signMessage(messageHashBytes) const encryptCommand: EncryptCommand = { blob: '1425252525', @@ -161,13 +161,12 @@ describe('Commands and handlers', () => { node ).getHandler(PROTOCOL_COMMANDS.ENCRYPT_FILE) nonce = (parseFloat(nonce) + 1).toString() - message = String(nonce) - consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes2 = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.ENCRYPT_FILE ) - messageHashBytes = ethers.toBeArray(consumerMessage) - signature = await consumerAccount.signMessage(messageHashBytes) + signature = await consumerAccount.signMessage(messageHashBytes2) const encryptFileCommand: EncryptFileCommand = { rawData: Buffer.from('12345'), command: PROTOCOL_COMMANDS.ENCRYPT_FILE, diff --git a/src/test/unit/download.test.ts b/src/test/unit/download.test.ts index da45d1d8d..20a02ff03 100644 --- a/src/test/unit/download.test.ts +++ b/src/test/unit/download.test.ts @@ -19,7 +19,8 @@ import { validateFilesStructure } from '../../components/core/handler/downloadHa import { AssetUtils, isConfidentialChainDDO } from '../../utils/asset.js' import { DEVELOPMENT_CHAIN_ID, KNOWN_CONFIDENTIAL_EVMS } from '../../utils/address.js' import { DDO } from '@oceanprotocol/ddo-js' -import { Wallet, ethers } from 'ethers' +import { Wallet } from 'ethers' +import { createHashForSignature } from '../utils/signature.js' let envOverrides: OverrideEnvConfig[] let config: OceanNodeConfig @@ -85,12 +86,11 @@ describe('Should validate files structure for download', () => { // and decrypts the data back to the original format const getDecryptedData = async function () { const nonce = Date.now().toString() - const message = String(nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + consumerAccount.address, + nonce, + PROTOCOL_COMMANDS.ENCRYPT ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await consumerAccount.signMessage(messageHashBytes) const result = await new EncryptHandler(oceanNode).handle({ blob: JSON.stringify(assetURL), @@ -149,12 +149,11 @@ describe('Should validate files structure for download', () => { newAssetURL.nftAddress = otherNFTAddress newAssetURL.datatokenAddress = otherDatatokenAddress const nonce = Date.now().toString() - const message = String(nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await consumerAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.ENCRYPT ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await consumerAccount.signMessage(messageHashBytes) const result = await new EncryptHandler(oceanNode).handle({ blob: JSON.stringify(newAssetURL), diff --git a/src/test/unit/indexer/validation.test.ts b/src/test/unit/indexer/validation.test.ts index 956668b4e..27cd526f3 100644 --- a/src/test/unit/indexer/validation.test.ts +++ b/src/test/unit/indexer/validation.test.ts @@ -20,6 +20,7 @@ import { OceanNodeConfig } from '../../../@types/OceanNode.js' // import sinon, { SinonSandbox } from 'sinon' import { ethers } from 'ethers' import { Readable } from 'stream' +import { createHashForSignature } from '../../utils/signature.js' describe('Schema validation tests', () => { const mockSupportedNetworks: RPCS = getMockSupportedNetworks() @@ -182,12 +183,11 @@ describe('Schema validation tests', () => { '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' ) const nonce = Date.now().toString() - const message = String((await wallet.getAddress()) + nonce) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] + const messageHashBytes = createHashForSignature( + await wallet.address, + nonce, + PROTOCOL_COMMANDS.VALIDATE_DDO ) - const messageHashBytes = ethers.toBeArray(consumerMessage) const signature = await wallet.signMessage(messageHashBytes) const task = { ddo, diff --git a/src/test/utils/signature.ts b/src/test/utils/signature.ts new file mode 100644 index 000000000..3f6da356e --- /dev/null +++ b/src/test/utils/signature.ts @@ -0,0 +1,28 @@ +import { ethers } from 'ethers' + +/** + * Generates a message hash for signing authentication requests to Ocean Protocol nodes. + * + * Creates a keccak256 hash of the concatenated consumer address, nonce, and command string + * using Solidity's packed encoding format. The resulting hash bytes can be signed by a wallet + * to authenticate requests. + * + * @param consumer - The consumer's Ethereum address (hex string) + * @param nonce - A unique nonce value (string or number) to prevent replay attacks + * @param command - The protocol command being requested (e.g., 'COMPUTE_START') + * @returns A Uint8Array containing the message hash bytes ready for signing + * + */ +export function createHashForSignature( + consumer: string, + nonce: string | number, + command: string +) { + const message = String(String(consumer) + String(nonce) + String(command)) + const consumerMessage = ethers.solidityPackedKeccak256( + ['bytes'], + [ethers.hexlify(ethers.toUtf8Bytes(message))] + ) + const messageHashBytes = ethers.toBeArray(consumerMessage) + return messageHashBytes +} diff --git a/src/utils/auth.ts b/src/utils/auth.ts index e3ccc3b4b..019bb8d82 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,84 +1,6 @@ -import { ethers, isAddress } from 'ethers' -import { CORE_LOGGER } from './logging/common.js' +import { isAddress } from 'ethers' import { getConfiguration } from './index.js' import { AccessListContract, OceanNodeConfig } from '../@types/OceanNode.js' -import { CommonValidation } from './validators.js' -import { isERC1271Valid } from '../components/core/utils/nonceHandler.js' -import { checkSingleCredential } from './credentials.js' -import { CREDENTIALS_TYPES } from '../@types/DDO/Credentials.js' -export async function validateAdminSignature( - expiryTimestamp: number, - signature: string, - address?: string -): Promise { - const message = expiryTimestamp.toString() - let signerAddress - - try { - const config = await getConfiguration() - if (address) { - const hexMessage = ethers.hashMessage(message) - const firstChainId = Object.keys(config?.supportedNetworks || {})[0] - if (firstChainId) { - const provider = new ethers.JsonRpcProvider( - config.supportedNetworks[firstChainId].rpc - ) - - if (!(await isERC1271Valid(address, hexMessage, signature, provider))) { - return { valid: false, error: 'Invalid ERC1271 signature' } - } - signerAddress = address - } else { - return { valid: false, error: 'No network configured in node config' } - } - } else { - signerAddress = ethers.verifyMessage(message, signature)?.toLowerCase() - CORE_LOGGER.logMessage(`Resolved signer address: ${signerAddress}`) - } - - const currentTimestamp = new Date().getTime() - if (currentTimestamp > expiryTimestamp) { - const errorMsg = `The expiryTimestamp ${expiryTimestamp} sent for validation is in the past. Therefore signature ${signature} is rejected` - CORE_LOGGER.logMessage(errorMsg) - return { valid: false, error: errorMsg } - } - const allowedAdmins = await getAdminAddresses(config) - - const { addresses, accessLists } = allowedAdmins - let allowed = await checkSingleCredential( - { type: CREDENTIALS_TYPES.ADDRESS, values: addresses }, - signerAddress, - null - ) - if (allowed) { - return { valid: true, error: '' } - } - if (accessLists) { - for (const chainId of Object.keys(accessLists)) { - allowed = await checkSingleCredential( - { - type: CREDENTIALS_TYPES.ACCESS_LIST, - chainId: parseInt(chainId), - accessList: accessLists[chainId] - }, - signerAddress, - null - ) - if (allowed) { - return { valid: true, error: '' } - } - } - } - - const errorMsg = `The address which signed the message is not on the allowed admins list. Therefore signature ${signature} is rejected` - CORE_LOGGER.logMessage(errorMsg) - return { valid: false, error: errorMsg } - } catch (e) { - const errorMsg = `Error during signature validation: ${e}` - CORE_LOGGER.error(errorMsg) - return { valid: false, error: errorMsg } - } -} export async function getAdminAddresses( existingConfig?: OceanNodeConfig From 38ac68671fd6d390a70da86a5cf9af3b4c48fd9d Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 11:11:23 +0200 Subject: [PATCH 02/30] fix typo --- src/test/integration/compute.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index fbf1d899a..2ce85ea8e 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -902,7 +902,7 @@ describe('Compute', () => { const messageHashBytes = createHashForSignature( await consumerAccount.getAddress(), nonce, - PROTOCOL_COMMANDS.COMPUTE_START + PROTOCOL_COMMANDS.FREE_COMPUTE_START ) const signature = await wallet.signMessage(messageHashBytes) const startComputeTask: FreeComputeStartCommand = { From f66f57514f85db8b5fc1e9ccf3a0432ddceca180 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 11:19:22 +0200 Subject: [PATCH 03/30] more fixes --- src/test/integration/compute.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index 2ce85ea8e..f984ce491 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -1007,7 +1007,7 @@ describe('Compute', () => { it('should get job result by additional viewer', async () => { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - await consumerAccount.getAddress(), + await wallet2.getAddress(), nonce, PROTOCOL_COMMANDS.COMPUTE_GET_RESULT ) @@ -1029,7 +1029,7 @@ describe('Compute', () => { it('should fail to get job result by non allowed address', async () => { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - await wallet3.address, + await wallet3.getAddress(), nonce, PROTOCOL_COMMANDS.COMPUTE_GET_RESULT ) From b6af6dddd60a221e845cc7081f2088cc9cfcea3a Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 11:49:58 +0200 Subject: [PATCH 04/30] more fixes --- src/components/core/admin/adminHandler.ts | 3 ++- src/test/integration/configAdmin.test.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/core/admin/adminHandler.ts b/src/components/core/admin/adminHandler.ts index 20bbdb825..e335d2c99 100644 --- a/src/components/core/admin/adminHandler.ts +++ b/src/components/core/admin/adminHandler.ts @@ -108,7 +108,8 @@ export abstract class AdminCommandHandler async validate(command: SignedCommand): Promise { const commandValidation = validateCommandParameters(command, [ - 'expiryTimestamp', + 'nonce', + 'address', 'signature' ]) if (!commandValidation.valid) { diff --git a/src/test/integration/configAdmin.test.ts b/src/test/integration/configAdmin.test.ts index 1cadda271..7cd498056 100644 --- a/src/test/integration/configAdmin.test.ts +++ b/src/test/integration/configAdmin.test.ts @@ -68,7 +68,7 @@ describe('Config Admin Endpoints Integration Tests', () => { const messageHashBytes = createHashForSignature( await adminAccount.getAddress(), nonce, - PROTOCOL_COMMANDS.STOP_NODE + command ) const signature = await adminAccount.signMessage(messageHashBytes) return signature From cd2514e3b8c7d5050c27425ee6a3337aa6be711b Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 12:14:18 +0200 Subject: [PATCH 05/30] more fixes --- src/test/integration/encryptDecryptDDO.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/integration/encryptDecryptDDO.test.ts b/src/test/integration/encryptDecryptDDO.test.ts index 2cdb2fbe7..7d75473c7 100644 --- a/src/test/integration/encryptDecryptDDO.test.ts +++ b/src/test/integration/encryptDecryptDDO.test.ts @@ -339,13 +339,13 @@ describe('Should encrypt and decrypt DDO', () => { it('should decrypt ddo with transactionId and return it', async () => { const nonce = Date.now().toString() - const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) + // const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) const messageHashBytes = createHashForSignature( - wallet.address, + await publisherAccount.getAddress(), nonce, PROTOCOL_COMMANDS.DECRYPT_DDO ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await publisherAccount.signMessage(messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, @@ -365,13 +365,13 @@ describe('Should encrypt and decrypt DDO', () => { it('should decrypt ddo with encryptedDocument, flags, documentHash and return it', async () => { const nonce = Date.now().toString() - const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) + // const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) const messageHashBytes = createHashForSignature( - wallet.address, + await publisherAccount.getAddress(), nonce, PROTOCOL_COMMANDS.DECRYPT_DDO ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await publisherAccount.signMessage(messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, From 8e7b310a99e941fccd28d42b897e566ed37ed2db Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 12:21:09 +0200 Subject: [PATCH 06/30] remove script --- src/helpers/scripts/clientExample.ts | 431 --------------------------- 1 file changed, 431 deletions(-) delete mode 100644 src/helpers/scripts/clientExample.ts diff --git a/src/helpers/scripts/clientExample.ts b/src/helpers/scripts/clientExample.ts deleted file mode 100644 index 69bd68eea..000000000 --- a/src/helpers/scripts/clientExample.ts +++ /dev/null @@ -1,431 +0,0 @@ -import axios from 'axios' -import fs from 'fs' - -import crypto from 'crypto' -import * as ethCrypto from 'eth-crypto' - -import * as Digest from 'multiformats/hashes/digest' -import { identity } from 'multiformats/hashes/identity' -import { base58btc } from 'multiformats/bases/base58' -// import { Wallet, ethers } from 'ethers' - -import pkg from 'secp256k1' -import { PROTOCOL_COMMANDS } from '../../utils/constants.js' -import { DownloadURLCommand } from '../../@types/commands.js' - -// Replace with any other file, works with a local path or URL -// '/var/log/syslog' -// See tests/unit/storage.test.ts for more examples, including IPFS and Arweave -const EXAMPLE_FILE = { - type: 'url', - url: 'https://ia800909.us.archive.org/13/items/CC_1917_04_16_TheCure/CC_1917_04_16_TheCure_512kb.mp4', - method: 'get' -} - -// AES encryption -const FILE_ENCRYPTION_ALGORITHM = 'aes-256-cbc' -const { publicKeyConvert } = pkg -// Decrypt the file after receiving it encrypted? -const DECRYPT_AFTER_RECEIVING_FILE = true - -// ######################################## -/** README/HOWTO - * - * How to run these examples: - * Open a terminal and setup node A (export port and private key) - Node A terminal - * Open a second terminal and setup node B (export port and private key) - Node B terminal - * Open a third terminal - this Client script - * 1) npm run start on terminal A - * 2) npm run start on terminal B - * 3) npm run client - * OBS: when making changes on the client or nodes, we always need to stop them, rebuild npm run build and restart them - * Have fun! - */ -// ######################################## -type P2PNode = { - node_id: string - port: number -} - -// Examples; take the node's private key from your local configuration -// and take the node public key (not needed) from the getP2pPeer API (node details) -// take the port from your local configuration as well -const nodeA: P2PNode = { - node_id: '16Uiu2HAkuYfgjXoGcSSLSpRPD6XtUgV71t5RqmTmcqdbmrWY9MJo', - port: 8000 -} - -const nodeB: P2PNode = { - node_id: '16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL', - port: 8001 -} - -type PeerDetails = { - nodeId: string - publicKey: string - valid: boolean -} - -type FileSecrets = { - privateKey: string // the private key used to decrypt the file - initVector: string // the initialization vector - encryptedKeyAndIV: string // the encrypted private key and iv, used for encrypting the file on Node side, as JSON string -} -// receive the node public key from the peer details, already validated before -/** - * - * @param nodePublicKey the node public key - * @param nodeBPrivateKey the node private key - * @returns - */ -async function createKeyPairForFileEncryption( - nodePublicKey: string -): Promise { - // We could also use Wallet variant - // let wallet = ethers.Wallet.createRandom(); - - // console.log('wallet private: ', wallet.privateKey); - // console.log('wallet public: ', wallet.publicKey) - // console.log('wallet address: ', wallet.address) - - const pair = ethCrypto.createIdentity() - console.log('private key:', pair.privateKey) - console.log('public key:', pair.publicKey) - console.log('address:', pair.address) - - const keyForAESFileEncryption = crypto.randomBytes(32) - const initializationVector = crypto.randomBytes(16) - console.log('keyForAESFileEncryption:', keyForAESFileEncryption.toString('hex')) - - console.log('initializationVector:', initializationVector.toString('hex')) - - // Symmetric key generation, for file encryption - const keySecrets = { - key: keyForAESFileEncryption.toString('hex'), - iv: initializationVector.toString('hex') - } - - const { privateKey } = pair // '0x3634cc4a3d2694a1186a7ce545f149e022eea103cc254d18d08675104bb4b5ac' - const secretMessage = JSON.stringify(keySecrets) // '92ecf27434c6140a5bdc6d62d972d5348e50aa6646db3a09e30ef4acc8616bef' - - console.log('Secret message JSON (before encryption): ' + secretMessage) - - const signature = ethCrypto.sign(privateKey, ethCrypto.hash.keccak256(secretMessage)) - const payload = { - message: secretMessage, - signature - } - - console.log('signature: ', signature) - console.log('payload: ', payload) - - const encryptedPayload = await ethCrypto.encryptWithPublicKey( - nodePublicKey, // by encrypting with target Node publicKey, only target Node can decrypt the payload with his privateKey - JSON.stringify(payload) // we have to stringify the payload before we can encrypt it - ) - - // we convert the object into a smaller string-representation - const encryptedAESKeyAndIV = ethCrypto.cipher.stringify(encryptedPayload) - - console.log('encrypted string/key:', encryptedAESKeyAndIV) - - // we parse the string into the object again - // const encryptedObject = ethCrypto.cipher.parse(encryptedAESKeyAndIV) - - // const decrypted = await ethCrypto.decryptWithPrivateKey(nodePrivateKey, encryptedObject) - // const decryptedPayload = JSON.parse(decrypted) - - // // check signature - // const senderAddress = ethCrypto.recover( - // decryptedPayload.signature, - // ethCrypto.hash.keccak256(decryptedPayload.message) - // ) - - // console.log('Got message from ' + senderAddress + ': ' + decryptedPayload.message) - - return { - privateKey: keySecrets.key, // we need to pass it here because we're generating the key and IV inside the function - initVector: keySecrets.iv, // same - encryptedKeyAndIV: encryptedAESKeyAndIV - } -} -/** - * Decrypt the file stream - * @param inputStream input stream to decrypt - * @param outputStream output stream to write to - * @param usePadding use padding? - * @param privateKey the key to use - * @param initVect the initialization vector - * @returns true if successful - */ -function decryptFileStream( - inputStream: any, - outputStream: any, - privateKey: string, - initVect: string -): Promise { - const decipher = crypto - .createDecipheriv( - FILE_ENCRYPTION_ALGORITHM, - Buffer.from(privateKey, 'hex'), - Buffer.from(initVect, 'hex') - ) - .setAutoPadding(true) - - // To see what is going on uncomment below - // decipher.on("data", chunk => { - // // console.log("Decrypting a file chunk of size: ", chunk.length) - // }) - - console.log('Decrypting key: ', privateKey) - console.log('Decrypting IV: ', initVect) - - return new Promise((resolve, reject) => { - // input => decryption => output - inputStream.pipe(decipher).pipe(outputStream) - - inputStream.on('end', () => { - resolve(true) - }) - - inputStream.on('error', (err: any) => { - reject(err) - }) - }) -} - -function testEchoCommand(): Promise { - return new Promise((resolve, reject) => { - axios({ - method: 'POST', - url: 'http://127.0.0.1:8000/directCommand', - responseType: 'stream', - data: { - command: 'echo', - // "node": "16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL", - // "16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL",//"16Uiu2HAkuYfgjXoGcSSLSpRPD6XtUgV71t5RqmTmcqdbmrWY9MJo", - url: 'http://example.com' - } - }) - .then(function (response: any) { - console.log('Got response from server...', response.status) - resolve(response.status === 200 ? 'OK' : 'NOK') - }) - .catch((err: any) => { - console.error('Error downloading....', err) - reject(err) - }) - }) -} - -function testDownloadCommand( - exampleId: number, // this is just to append to file name for testing purposes - nodeHttpPort: number, - nodeId?: string, - fileSecrets?: FileSecrets -): Promise { - const payload: DownloadURLCommand = { - // node: '16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL', // IF not present use own node - // own node A is: "16Uiu2HAkuYfgjXoGcSSLSpRPD6XtUgV71t5RqmTmcqdbmrWY9MJo", - // other node B is: 16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL - fileObject: EXAMPLE_FILE, // http://example.com' - command: PROTOCOL_COMMANDS.DOWNLOAD_URL - // "aes_encrypted_key": encryptedAESKeyAndIV - } - - const useEncryption = fileSecrets && fileSecrets.encryptedKeyAndIV - - if (useEncryption) { - payload.aes_encrypted_key = fileSecrets.encryptedKeyAndIV - } - if (nodeId) { - payload.node = nodeId - } - - return new Promise((resolve, reject) => { - axios({ - method: 'POST', - url: `http://127.0.0.1:${nodeHttpPort}/directCommand`, - responseType: 'stream', - data: payload - }) - .then(function (response: any) { - // console.log('Got response from server...', response.data) - - const fileOutput = './dist/helpers/scripts/output/received_out_' - let suffix = '' + exampleId - if (useEncryption) { - suffix = suffix + (DECRYPT_AFTER_RECEIVING_FILE ? '.decoded' : '.encoded') - } - - // eslint-disable-next-line security/detect-non-literal-fs-filename - const out = fs.createWriteStream(fileOutput + suffix) - - if (useEncryption && DECRYPT_AFTER_RECEIVING_FILE) { - // decrypt the stream and store it decrypted already - // if we want to store it encrypted just do as bellow: - // response.data.pipe(fileOutput) - decryptFileStream( - response.data, - out, - fileSecrets.privateKey, - fileSecrets.initVector - ) - .then(() => { - console.log('File decoded successfully locally!') - console.log('File download complete') - resolve(fileOutput) - }) - .catch((err) => { - console.log('Error decrypting the file: ', err) - }) - } else { - // response.data.on('data', function (chunk: any) { - // console.log('Got chunk: ', chunk); - // }); - - out.on('close', function () { - console.log('Stream Ended! Saved file to path: ', fileOutput) - // will decode the file now - resolve(fileOutput) - }) - - // just write the file output - response.data.pipe(out) - } - }) - .catch((err: any) => { - console.error('Error downloading....', err) - reject(err) - }) - }) -} - -async function getPeerDetails(url: string, nodeId: string): Promise { - const response = await axios.get(url, { - params: { - peerId: nodeId - }, - headers: { - Accept: 'text/json' - } - }) - - const data = await response.data - console.log('Got data from server:', data) - // get the PeerDetails - const details: PeerDetails = { - nodeId: data.id, - publicKey: data.publicKey, - valid: false - } - - // get public key - const hexString: string = details.publicKey - // convert it to byte array - const publicKeyBytes: Uint8Array = Uint8Array.from(Buffer.from(hexString, 'hex')) - - // same hash that is done on peers to get a node identifier (they strip 1st byte) - const multihash: any = await Digest.create(identity.code, publicKeyBytes) - const generatedNodeId: string = base58btc.encode(multihash.bytes).slice(1) - console.log('generatedNodeId', generatedNodeId) - - // confirm that this public key originated this nodeId - const isNodeValid = generatedNodeId === details.nodeId - - console.log('isNodeValid:', isNodeValid) - - // update details info with the verification outcome - details.valid = isNodeValid - - // console.log('Got PeerDetails:', details) - - return details -} - -async function testEncryptionFlow( - exampleId: number, - nodeHttpPort: number, - targetNodeId: string -): Promise { - // get the peer details and validate them to check if public key matches nodeId - - const url = `http://127.0.0.1:${nodeHttpPort}/getP2pPeer` - console.log('url ', url) - console.log('target id ', targetNodeId) - const peerDetails = await getPeerDetails( - `http://127.0.0.1:${nodeHttpPort}/getP2pPeer`, - targetNodeId - ) - console.log('Got Peer details:', peerDetails) - if (peerDetails.valid) { - const compressedPublicKey = peerDetails.publicKey - console.log('Compressed public key:', compressedPublicKey) - const hexArray: Uint8Array = Uint8Array.from(Buffer.from(compressedPublicKey, 'hex')) - // console.log('hexArray:', hexArray) - // console.log('hexArray sliced:', Buffer.from(hexArray.slice(4)).toString('hex')) - const decompressedKeyArray: Uint8Array = publicKeyConvert(hexArray.slice(4), false) - const decompressedPublicKey = Buffer.from(decompressedKeyArray).toString('hex') - console.log('Decompressed public key:', decompressedPublicKey) - - // This DOES NOT WORK!! - // const decompressedPublicKeyWithEthers = ethers.utils.computePublicKey( - // hexArray.slice(4), - // true - // ) - // console.log( - // 'decompressed public key with ethers:', - // Buffer.from(decompressedPublicKeyWithEthers).toString('hex') - // ) - // get the AES key and IV encrypted with node public key - const secrets: FileSecrets = - await createKeyPairForFileEncryption(decompressedPublicKey) - - console.log('Will download now: ') - console.log('exampleId: ', exampleId) - console.log('nodeHttpPort: ', nodeHttpPort) - console.log('targetNodeId: ', targetNodeId) - - // send command to Node - await testDownloadCommand(exampleId, nodeHttpPort, targetNodeId, secrets) - } -} - -const status = await testEchoCommand() -console.log('Echo command status: ', status) - -// In the following examples we always connect to Node A HTTP interface. -// Client = this script -// nodeA = The node we are connecting to via HTTP -// nodeB = The node that will be reached via P2P from nodeA - -// On the 1s case Node A can serve the file directly to the client -// On 2nd case Node A will act as a proxy betweeen Node B and the Client - -// ################# example 1 #################### -// Ex: 1 - Request directly to Node A -// Still goes to the same validation and encryption processes, the only difference is that Node A can serve directly to Client -await testEncryptionFlow(1, nodeA.port, nodeA.node_id) -// ################################################ - -// ################# example 2 #################### -// Ex: 2 - Request file from Node B, via Node A -// The file will be encrypted on node B, using the AES key/IV randomly generated on client -// Client get the peer detaisl and validates the node id agains the public key of the node -// Client encrypts the AES key and IV with node public key, and sends the HTTP request -// Node A forwards the request to Node B, Node B decrypts the AES key and IV with his private key, and encrypts the file -// Node B streams encrypted file to Node A, Node A CANNOT decrypt anything and forwards the request to the initial Client -await testEncryptionFlow(2, nodeA.port, nodeB.node_id) -// ################################################ - -// ################ example 3 ##################### -// On the following example we swap the node roles -// Example 3 (swap nodes) -// We connect to Node B via HTTP and try to get a file from node A, instead -await testEncryptionFlow(3, nodeB.port, nodeA.node_id) -// ############################################### - -// ################# example 4 #################### -// Example 4 - Same as example 1) above but without encryption -await testDownloadCommand(4, nodeA.port, nodeA.node_id) -// ################################################ From f847140daaf490649cee7c8c6b71ad3ec1fe65dc Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 12:21:31 +0200 Subject: [PATCH 07/30] remove command DOWNLOAD_URL, use DOWNLOAD --- src/components/core/handler/downloadHandler.ts | 2 +- src/utils/constants.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/core/handler/downloadHandler.ts b/src/components/core/handler/downloadHandler.ts index 1d95d1dce..beadf9e16 100644 --- a/src/components/core/handler/downloadHandler.ts +++ b/src/components/core/handler/downloadHandler.ts @@ -531,7 +531,7 @@ export class DownloadHandler extends CommandHandler { return await handleDownloadUrlCommand(node, { fileObject: decriptedFileObject, aes_encrypted_key: task.aes_encrypted_key, - command: PROTOCOL_COMMANDS.DOWNLOAD_URL + command: PROTOCOL_COMMANDS.DOWNLOAD }) } catch (e) { CORE_LOGGER.logMessage('Decryption error: ' + e, true) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 8566fbe4e..88aef25b8 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -3,7 +3,6 @@ import { Hashes } from '../@types/blockchain' // Add all the supported commands export const PROTOCOL_COMMANDS = { DOWNLOAD: 'download', - DOWNLOAD_URL: 'downloadURL', // we still use this ENCRYPT: 'encrypt', ENCRYPT_FILE: 'encryptFile', DECRYPT_DDO: 'decryptDDO', From 30364e8716e8666bcc069a45cecd8cd3c0f0ee58 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 12:41:20 +0200 Subject: [PATCH 08/30] more fixes --- src/test/integration/encryptDecryptDDO.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/integration/encryptDecryptDDO.test.ts b/src/test/integration/encryptDecryptDDO.test.ts index 7d75473c7..aedb45606 100644 --- a/src/test/integration/encryptDecryptDDO.test.ts +++ b/src/test/integration/encryptDecryptDDO.test.ts @@ -339,13 +339,14 @@ describe('Should encrypt and decrypt DDO', () => { it('should decrypt ddo with transactionId and return it', async () => { const nonce = Date.now().toString() - // const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) + // wallet is the same account as publisherAccount, but supports personalSign, while publisherAccount does not + const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) const messageHashBytes = createHashForSignature( await publisherAccount.getAddress(), nonce, PROTOCOL_COMMANDS.DECRYPT_DDO ) - const signature = await publisherAccount.signMessage(messageHashBytes) + const signature = await wallet.signMessage(messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, @@ -365,13 +366,14 @@ describe('Should encrypt and decrypt DDO', () => { it('should decrypt ddo with encryptedDocument, flags, documentHash and return it', async () => { const nonce = Date.now().toString() - // const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) + // wallet is the same account as publisherAccount, but supports personalSign, while publisherAccount does not + const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) const messageHashBytes = createHashForSignature( await publisherAccount.getAddress(), nonce, PROTOCOL_COMMANDS.DECRYPT_DDO ) - const signature = await publisherAccount.signMessage(messageHashBytes) + const signature = await wallet.signMessage(messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, From 5df17bd9dacf72af60938f26b73cd6766d0e1729 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 13:56:50 +0200 Subject: [PATCH 09/30] more fixes --- src/components/core/handler/ddoHandler.ts | 109 ++++----------- .../core/handler/downloadHandler.ts | 6 +- src/test/integration/algorithmsAccess.test.ts | 9 +- src/test/integration/compute.test.ts | 73 +++++----- .../integration/encryptDecryptDDO.test.ts | 129 ++++++++++-------- src/test/utils/signature.ts | 27 +++- 6 files changed, 173 insertions(+), 180 deletions(-) diff --git a/src/components/core/handler/ddoHandler.ts b/src/components/core/handler/ddoHandler.ts index 539f7fa7b..622cbbb5c 100644 --- a/src/components/core/handler/ddoHandler.ts +++ b/src/components/core/handler/ddoHandler.ts @@ -70,6 +70,32 @@ export class DecryptDdoHandler extends CommandHandler { if (this.shouldDenyTaskHandling(validationResponse)) { return validationResponse } + const chainId = String(task.chainId) + const config = await getConfiguration() + const supportedNetwork = config.supportedNetworks[chainId] + + // check if supported chainId + if (!supportedNetwork) { + CORE_LOGGER.logMessage(`Decrypt DDO: Unsupported chain id ${chainId}`, true) + return { + stream: null, + status: { + httpStatus: 400, + error: `Decrypt DDO: Unsupported chain id` + } + } + } + const isAuthRequestValid = await this.validateTokenOrSignature( + task.authorization, + task.decrypterAddress, + task.nonce, + task.signature, + task.command + ) + if (isAuthRequestValid.status.httpStatus !== 200) { + return isAuthRequestValid + } + try { let decrypterAddress: string try { @@ -85,52 +111,6 @@ export class DecryptDdoHandler extends CommandHandler { } } - const nonce = Number(task.nonce) - if (isNaN(nonce)) { - CORE_LOGGER.logMessage( - `Decrypt DDO: error ${task.nonce} value is not a number`, - true - ) - return { - stream: null, - status: { - httpStatus: 400, - error: `Decrypt DDO: nonce value is not a number` - } - } - } - - const node = this.getOceanNode() - const dbNonce = node.getDatabase().nonce - const existingNonce = await dbNonce.retrieve(decrypterAddress) - - if (existingNonce && existingNonce.nonce === nonce) { - CORE_LOGGER.logMessage(`Decrypt DDO: error ${task.nonce} duplicate nonce`, true) - return { - stream: null, - status: { - httpStatus: 400, - error: `Decrypt DDO: duplicate nonce` - } - } - } - - await dbNonce.update(decrypterAddress, nonce) - const chainId = String(task.chainId) - const config = await getConfiguration() - const supportedNetwork = config.supportedNetworks[chainId] - - // check if supported chainId - if (!supportedNetwork) { - CORE_LOGGER.logMessage(`Decrypt DDO: Unsupported chain id ${chainId}`, true) - return { - stream: null, - status: { - httpStatus: 400, - error: `Decrypt DDO: Unsupported chain id` - } - } - } const ourEthAddress = this.getOceanNode().getKeyManager().getEthAddress() if (config.authorizedDecrypters.length > 0) { // allow if on authorized list or it is own node @@ -150,7 +130,6 @@ export class DecryptDdoHandler extends CommandHandler { } } } - const oceanNode = this.getOceanNode() const blockchain = oceanNode.getBlockchain(supportedNetwork.chainId) if (!blockchain) { @@ -186,7 +165,6 @@ export class DecryptDdoHandler extends CommandHandler { signer, dataNftAddress ) - if (!wasDeployedByUs) { CORE_LOGGER.logMessage( 'Decrypt DDO: Asset not deployed by the data NFT factory', @@ -387,41 +365,6 @@ export class DecryptDdoHandler extends CommandHandler { } } - // check signature - try { - const useTxIdOrContractAddress = transactionId || dataNftAddress - - const message = String( - useTxIdOrContractAddress + decrypterAddress + chainId + nonce - ) - const messageHash = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] - ) - const messageHashBytes = ethers.getBytes(messageHash) - const addressFromHashSignature = ethers.verifyMessage(messageHash, task.signature) - const addressFromBytesSignature = ethers.verifyMessage( - messageHashBytes, - task.signature - ) - - if ( - addressFromHashSignature?.toLowerCase() !== decrypterAddress?.toLowerCase() && - addressFromBytesSignature?.toLowerCase() !== decrypterAddress?.toLowerCase() - ) { - throw new Error('address does not match') - } - } catch (error) { - CORE_LOGGER.logMessage(`Decrypt DDO: error signature ${error}`, true) - return { - stream: null, - status: { - httpStatus: 400, - error: 'Decrypt DDO: invalid signature or does not match' - } - } - } - return { stream: Readable.from(decryptedDocument.toString()), status: { httpStatus: 200 } diff --git a/src/components/core/handler/downloadHandler.ts b/src/components/core/handler/downloadHandler.ts index beadf9e16..8358ab490 100644 --- a/src/components/core/handler/downloadHandler.ts +++ b/src/components/core/handler/downloadHandler.ts @@ -186,6 +186,9 @@ export class DownloadHandler extends CommandHandler { async handle(task: DownloadCommand): Promise { const validationResponse = await this.verifyParamsAndRateLimits(task) + if (this.shouldDenyTaskHandling(validationResponse)) { + return validationResponse + } const isAuthRequestValid = await this.validateTokenOrSignature( task.authorization, task.consumerAddress, @@ -197,9 +200,6 @@ export class DownloadHandler extends CommandHandler { return isAuthRequestValid } - if (this.shouldDenyTaskHandling(validationResponse)) { - return validationResponse - } const node = this.getOceanNode() // 1. Get the DDO const handler: FindDdoHandler = node diff --git a/src/test/integration/algorithmsAccess.test.ts b/src/test/integration/algorithmsAccess.test.ts index 8fbf6165f..7af1a9b07 100644 --- a/src/test/integration/algorithmsAccess.test.ts +++ b/src/test/integration/algorithmsAccess.test.ts @@ -48,7 +48,7 @@ import OceanToken from '@oceanprotocol/contracts/artifacts/contracts/utils/Ocean import EscrowJson from '@oceanprotocol/contracts/artifacts/contracts/escrow/Escrow.sol/Escrow.json' with { type: 'json' } import { createHash } from 'crypto' import { getAlgoChecksums } from '../../components/core/compute/utils.js' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('Trusted algorithms Flow', () => { let previousConfiguration: OverrideEnvConfig[] @@ -72,9 +72,6 @@ describe('Trusted algorithms Flow', () => { const computeJobDuration = 60 * 15 // 15 minutes from now should be enough let firstEnv: ComputeEnvironment - const wallet = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - ) // const chainId = DEVELOPMENT_CHAIN_ID const mockSupportedNetworks: RPCS = getMockSupportedNetworks() let artifactsAddresses: any @@ -435,11 +432,11 @@ describe('Trusted algorithms Flow', () => { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await consumerAccount.getAddress(), nonce, PROTOCOL_COMMANDS.COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const startComputeTask: PaidComputeStartCommand = { command: PROTOCOL_COMMANDS.COMPUTE_START, consumerAddress: await consumerAccount.getAddress(), diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index f984ce491..ae0e29277 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -80,7 +80,7 @@ import { freeComputeStartPayload } from '../data/commands.js' import { DDOManager } from '@oceanprotocol/ddo-js' import Dockerode from 'dockerode' import { C2DEngineDocker } from '../../components/c2d/compute_engine_docker.js' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) @@ -92,6 +92,8 @@ describe('Compute', () => { let provider: any let publisherAccount: any let consumerAccount: any + let additionalViewerAccount: any + let nonAllowedAccount: any let computeEnvironments: any let publishedComputeDataset: any let publishedAlgoDataset: any @@ -109,7 +111,7 @@ describe('Compute', () => { const computeJobDuration = 60 * 15 // 15 minutes from now should be enough let firstEnv: ComputeEnvironment - const wallet = new ethers.Wallet( + /* const wallet = new ethers.Wallet( '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' ) const wallet2 = new ethers.Wallet( @@ -117,7 +119,7 @@ describe('Compute', () => { ) const wallet3 = new ethers.Wallet( '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d4521A' - ) + ) */ // const chainId = DEVELOPMENT_CHAIN_ID const mockSupportedNetworks: RPCS = getMockSupportedNetworks() @@ -184,6 +186,8 @@ describe('Compute', () => { provider = new JsonRpcProvider('http://127.0.0.1:8545') publisherAccount = (await provider.getSigner(0)) as Signer consumerAccount = (await provider.getSigner(1)) as Signer + additionalViewerAccount = (await provider.getSigner(2)) as Signer + nonAllowedAccount = (await provider.getSigner(3)) as Signer publisherAddress = await publisherAccount.getAddress() algoDDO = { ...publishAlgoDDO } @@ -613,7 +617,7 @@ describe('Compute', () => { PROTOCOL_COMMANDS.COMPUTE_START ) // since ganache does not supports personal_sign, we use wallet account - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const startComputeTask: PaidComputeStartCommand = { command: PROTOCOL_COMMANDS.COMPUTE_START, consumerAddress: await consumerAccount.getAddress(), @@ -710,7 +714,7 @@ describe('Compute', () => { nonce, PROTOCOL_COMMANDS.COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const re = [] for (const res of firstEnv.resources) { re.push({ id: res.id, amount: res.total }) @@ -742,7 +746,7 @@ describe('Compute', () => { metadata: { key: 'value' }, - additionalViewers: [await wallet2.getAddress()], + additionalViewers: [await additionalViewerAccount.getAddress()], maxJobDuration: computeJobDuration, resources: re // additionalDatasets?: ComputeAsset[] @@ -798,7 +802,7 @@ describe('Compute', () => { nonce2, PROTOCOL_COMMANDS.COMPUTE_START ) - const signature2 = await wallet.signMessage(messageHashBytes2) + const signature2 = await safeSign(consumerAccount, messageHashBytes2) response = await new PaidComputeStartHandler(oceanNode).handle({ ...startComputeTask, nonce: nonce2, @@ -856,7 +860,7 @@ describe('Compute', () => { nonce, PROTOCOL_COMMANDS.COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const re = [] for (const res of firstEnv.resources) { re.push({ id: res.id, amount: res.total }) @@ -904,10 +908,10 @@ describe('Compute', () => { nonce, PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const startComputeTask: FreeComputeStartCommand = { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, - consumerAddress: await wallet.getAddress(), + consumerAddress: await consumerAccount.getAddress(), signature, nonce, environment: firstEnv.id, @@ -989,10 +993,10 @@ describe('Compute', () => { nonce, PROTOCOL_COMMANDS.COMPUTE_GET_RESULT ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const resultComputeTask: ComputeGetResultCommand = { command: PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, - consumerAddress: await wallet.getAddress(), + consumerAddress: await consumerAccount.getAddress(), jobId, signature, nonce, @@ -1007,14 +1011,14 @@ describe('Compute', () => { it('should get job result by additional viewer', async () => { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - await wallet2.getAddress(), + await additionalViewerAccount.getAddress(), nonce, PROTOCOL_COMMANDS.COMPUTE_GET_RESULT ) - const signature = await wallet2.signMessage(messageHashBytes) + const signature = await safeSign(additionalViewerAccount, messageHashBytes) const resultComputeTask: ComputeGetResultCommand = { command: PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, - consumerAddress: await wallet2.getAddress(), + consumerAddress: await additionalViewerAccount.getAddress(), jobId, signature, nonce, @@ -1029,14 +1033,14 @@ describe('Compute', () => { it('should fail to get job result by non allowed address', async () => { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - await wallet3.getAddress(), + await nonAllowedAccount.getAddress(), nonce, PROTOCOL_COMMANDS.COMPUTE_GET_RESULT ) - const signature = await wallet3.signMessage(messageHashBytes) + const signature = await safeSign(nonAllowedAccount, messageHashBytes) const resultComputeTask: ComputeGetResultCommand = { command: PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, - consumerAddress: await wallet3.getAddress(), + consumerAddress: await nonAllowedAccount.getAddress(), jobId, signature, nonce, @@ -1058,7 +1062,7 @@ describe('Compute', () => { nonce, PROTOCOL_COMMANDS.COMPUTE_STOP ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const stopComputeTask: ComputeStopCommand = { command: PROTOCOL_COMMANDS.COMPUTE_STOP, consumerAddress: await consumerAccount.getAddress(), @@ -1106,16 +1110,16 @@ describe('Compute', () => { it('should deny the Free job due to bad container image (directCommand payload)', async function () { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - await wallet.address, + await consumerAccount.address, nonce, PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) freeComputeStartPayload.command = PROTOCOL_COMMANDS.FREE_COMPUTE_START freeComputeStartPayload.signature = signature freeComputeStartPayload.nonce = nonce freeComputeStartPayload.environment = firstEnv.id - freeComputeStartPayload.consumerAddress = await wallet.getAddress() + freeComputeStartPayload.consumerAddress = await consumerAccount.getAddress() const command: FreeComputeStartCommand = freeComputeStartPayload const handler = new FreeComputeStartHandler(oceanNode) const response = await handler.handle(command) @@ -1587,8 +1591,7 @@ describe('Compute', () => { nonce, PROTOCOL_COMMANDS.COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) - + const signature = await safeSign(consumerAccount, messageHashBytes) const startComputeTask: PaidComputeStartCommand = { command: PROTOCOL_COMMANDS.COMPUTE_START, consumerAddress: await consumerAccount.getAddress(), @@ -1646,7 +1649,7 @@ describe('Compute', () => { nonce, PROTOCOL_COMMANDS.COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const startComputeTask: PaidComputeStartCommand = { command: PROTOCOL_COMMANDS.COMPUTE_START, @@ -1695,15 +1698,15 @@ describe('Compute', () => { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - await wallet.address, + await consumerAccount.address, nonce, PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const startComputeTask: FreeComputeStartCommand = { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, - consumerAddress: await wallet.getAddress(), + consumerAddress: await consumerAccount.getAddress(), signature, nonce, environment: firstEnv.id, @@ -1756,11 +1759,11 @@ describe('Compute', () => { nonce, PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const startComputeTask: FreeComputeStartCommand = { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, - consumerAddress: await wallet.getAddress(), + consumerAddress: await consumerAccount.getAddress(), signature, nonce, environment: firstEnv.id, @@ -2012,8 +2015,10 @@ describe('Compute Access Restrictions', () => { let paymentToken: any let firstEnv: ComputeEnvironment let accessListAddress: string - - const wallet = new ethers.Wallet( + let wallet: any + let wallet2: any + let wallet3: any + /* const wallet = new ethers.Wallet( '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' ) const wallet2 = new ethers.Wallet( @@ -2022,6 +2027,7 @@ describe('Compute Access Restrictions', () => { const wallet3 = new ethers.Wallet( '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d4521A' ) + */ const mockSupportedNetworks: RPCS = getMockSupportedNetworks() const computeJobDuration = 60 * 15 @@ -2154,6 +2160,9 @@ describe('Compute Access Restrictions', () => { provider = new JsonRpcProvider('http://127.0.0.1:8545') publisherAccount = await provider.getSigner(0) + wallet = await provider.getSigner(1) + wallet2 = await provider.getSigner(2) + wallet3 = await provider.getSigner(3) publishedComputeDataset = await publishAsset(computeAsset, publisherAccount) publishedAlgoDataset = await publishAsset(algoAsset, publisherAccount) diff --git a/src/test/integration/encryptDecryptDDO.test.ts b/src/test/integration/encryptDecryptDDO.test.ts index aedb45606..8f115727b 100644 --- a/src/test/integration/encryptDecryptDDO.test.ts +++ b/src/test/integration/encryptDecryptDDO.test.ts @@ -39,7 +39,7 @@ import { DecryptDDOCommand } from '../../@types/commands.js' import { EncryptMethod } from '../../@types/fileObject.js' import { homedir } from 'os' import { OceanIndexer } from '../../components/Indexer/index.js' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('Should encrypt and decrypt DDO', () => { let database: Database @@ -47,6 +47,7 @@ describe('Should encrypt and decrypt DDO', () => { let provider: JsonRpcProvider let publisherAccount: Signer let publisherAddress: string + let nonAllowedAccount: Signer let factoryContract: Contract let nftContract: Contract let dataNftAddress: string @@ -74,6 +75,8 @@ describe('Should encrypt and decrypt DDO', () => { provider = new JsonRpcProvider('http://127.0.0.1:8545') publisherAccount = (await provider.getSigner(0)) as Signer publisherAddress = await publisherAccount.getAddress() + nonAllowedAccount = (await provider.getSigner(1)) as Signer + genericAsset = genericDDO previousConfiguration = await setupEnvironment( TEST_ENV_CONFIG_FILE, @@ -192,26 +195,21 @@ describe('Should encrypt and decrypt DDO', () => { expect(response.status.error).to.include('Decrypt DDO: Unsupported chain id') }) - it('should return error duplicate nonce', async () => { - const decryptDDOTask: DecryptDDOCommand = { - command: PROTOCOL_COMMANDS.DECRYPT_DDO, - decrypterAddress: publisherAddress, - chainId: 8996, + it('should return decrypter not authorized', async () => { + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + await nonAllowedAccount.getAddress(), nonce, - signature: '0x123' - } - const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) - expect(response.status.httpStatus).to.equal(400) - expect(response.status.error).to.equal(`Decrypt DDO: duplicate nonce`) - }) + PROTOCOL_COMMANDS.DECRYPT_DDO + ) + const signature = await safeSign(nonAllowedAccount, messageHashBytes) - it('should return decrypter not authorized', async () => { const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, - decrypterAddress: '0x0000000000000000000000000000000000000001', + decrypterAddress: await nonAllowedAccount.getAddress(), chainId, - nonce: Date.now().toString(), - signature: '0x123' + nonce, + signature } const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) expect(response.status.httpStatus).to.equal(403) @@ -219,26 +217,43 @@ describe('Should encrypt and decrypt DDO', () => { }) it('should authorize decrypter since is this node', async () => { + const nonce = Date.now().toString() + const thisNodeWallet = oceanNode.getKeyManager().getEthWallet() + const messageHashBytes = createHashForSignature( + await thisNodeWallet.getAddress(), + nonce, + PROTOCOL_COMMANDS.DECRYPT_DDO + ) + const signature = await safeSign(thisNodeWallet, messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, decrypterAddress: oceanNode.getKeyManager().getEthAddress(), chainId, - nonce: Date.now().toString(), - signature: '0x123' + nonce, + signature, + dataNftAddress, + transactionId: txReceiptEncryptDDO.hash } const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) - expect(response.status.httpStatus).to.not.equal(403) - expect(response.status.error).to.not.equal('Decrypt DDO: Decrypter not authorized') + expect(response.status.httpStatus).to.equal(200) + // expect(response.status.error).to.not.equal('Decrypt DDO: Decrypter not authorized') }) it('should return asset not deployed by the data NFT factory', async () => { + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + await publisherAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.DECRYPT_DDO + ) + const signature = await safeSign(publisherAccount, messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, decrypterAddress: publisherAddress, chainId, dataNftAddress: publisherAddress, - nonce: Date.now().toString(), - signature: '0x123' + nonce, + signature } const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) expect(response.status.httpStatus).to.equal(400) @@ -248,14 +263,21 @@ describe('Should encrypt and decrypt DDO', () => { }) it('should return failed to process transaction id', async () => { + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + await publisherAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.DECRYPT_DDO + ) + const signature = await safeSign(publisherAccount, messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, decrypterAddress: publisherAddress, chainId, transactionId: 'string', dataNftAddress, - nonce: Date.now().toString(), - signature: '0x123' + nonce, + signature } const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) expect(response.status.httpStatus).to.equal(400) @@ -265,6 +287,13 @@ describe('Should encrypt and decrypt DDO', () => { }) it('should return failed to convert input args to bytes', async () => { + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + await publisherAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.DECRYPT_DDO + ) + const signature = await safeSign(publisherAccount, messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, decrypterAddress: publisherAddress, @@ -273,8 +302,8 @@ describe('Should encrypt and decrypt DDO', () => { flags: 1, documentHash: '123', dataNftAddress, - nonce: Date.now().toString(), - signature: '0x123' + nonce, + signature } const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) expect(response.status.httpStatus).to.equal(400) @@ -284,6 +313,13 @@ describe('Should encrypt and decrypt DDO', () => { }) it('should return data NFT factory does not match', async () => { + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + await publisherAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.DECRYPT_DDO + ) + const signature = await safeSign(publisherAccount, messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, decrypterAddress: publisherAddress, @@ -292,8 +328,8 @@ describe('Should encrypt and decrypt DDO', () => { flags: 2, documentHash: '0x123', dataNftAddress: '0x0000000000000000000000000000000000000001', - nonce: Date.now().toString(), - signature: '0x123' + nonce, + signature } const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) expect(response.status.httpStatus).to.equal(400) @@ -303,6 +339,13 @@ describe('Should encrypt and decrypt DDO', () => { }) it('should return checksum does not match', async () => { + const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( + await publisherAccount.getAddress(), + nonce, + PROTOCOL_COMMANDS.DECRYPT_DDO + ) + const signature = await safeSign(publisherAccount, messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, decrypterAddress: publisherAddress, @@ -311,42 +354,22 @@ describe('Should encrypt and decrypt DDO', () => { flags: 2, documentHash: '0x123', dataNftAddress, - nonce: Date.now().toString(), - signature: '0x123' + nonce, + signature } const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) expect(response.status.httpStatus).to.equal(400) expect(response.status.error).to.equal('Decrypt DDO: checksum does not match') }) - it('should return signature does not match', async () => { - const decryptDDOTask: DecryptDDOCommand = { - command: PROTOCOL_COMMANDS.DECRYPT_DDO, - decrypterAddress: publisherAddress, - chainId, - transactionId: txReceiptEncryptDDO.hash, - dataNftAddress, - nonce: Date.now().toString(), - documentHash, - signature: '0x123' - } - const response = await new DecryptDdoHandler(oceanNode).handle(decryptDDOTask) - expect(response.status.httpStatus).to.equal(400) - expect(response.status.error).to.equal( - 'Decrypt DDO: invalid signature or does not match' - ) - }) - it('should decrypt ddo with transactionId and return it', async () => { const nonce = Date.now().toString() - // wallet is the same account as publisherAccount, but supports personalSign, while publisherAccount does not - const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) const messageHashBytes = createHashForSignature( await publisherAccount.getAddress(), nonce, PROTOCOL_COMMANDS.DECRYPT_DDO ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(publisherAccount, messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, @@ -366,14 +389,12 @@ describe('Should encrypt and decrypt DDO', () => { it('should decrypt ddo with encryptedDocument, flags, documentHash and return it', async () => { const nonce = Date.now().toString() - // wallet is the same account as publisherAccount, but supports personalSign, while publisherAccount does not - const wallet = new ethers.Wallet(process.env.PRIVATE_KEY) const messageHashBytes = createHashForSignature( await publisherAccount.getAddress(), nonce, PROTOCOL_COMMANDS.DECRYPT_DDO ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(publisherAccount, messageHashBytes) const decryptDDOTask: DecryptDDOCommand = { command: PROTOCOL_COMMANDS.DECRYPT_DDO, diff --git a/src/test/utils/signature.ts b/src/test/utils/signature.ts index 3f6da356e..9706b516f 100644 --- a/src/test/utils/signature.ts +++ b/src/test/utils/signature.ts @@ -1,4 +1,4 @@ -import { ethers } from 'ethers' +import { ethers, Signer, JsonRpcSigner } from 'ethers' /** * Generates a message hash for signing authentication requests to Ocean Protocol nodes. @@ -17,7 +17,7 @@ export function createHashForSignature( consumer: string, nonce: string | number, command: string -) { +): Uint8Array { const message = String(String(consumer) + String(nonce) + String(command)) const consumerMessage = ethers.solidityPackedKeccak256( ['bytes'], @@ -26,3 +26,26 @@ export function createHashForSignature( const messageHashBytes = ethers.toBeArray(consumerMessage) return messageHashBytes } + +/** + * Safe sign a message. + * @param {Signer} signer - The signer to use. + * @param {string} messageHash - The message to sign. + * @returns {Promise} A promise that resolves with the signature. + */ +export async function safeSign( + signer: Signer, + messageHashBytes: Uint8Array +): Promise { + try { + return await signer.signMessage(messageHashBytes) + } catch (error) { + // LoggerInstance.error('Sign provider message error: ', error) + // Check if the user is using barge chain + const network = await signer.provider.getNetwork() + const chainId = Number(network.chainId) + if (chainId === 8996) { + return await (signer as JsonRpcSigner)._legacySignMessage(messageHashBytes) + } + } +} From 0c8080f58efe07cd28ce24482ab9b69467f74801 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 14:12:02 +0200 Subject: [PATCH 10/30] fix --- src/test/integration/compute.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index ae0e29277..b9d4da6c9 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -2108,6 +2108,9 @@ describe('Compute Access Restrictions', () => { before(async () => { const artifactsAddresses = getOceanArtifactsAdresses() paymentToken = artifactsAddresses.development.Ocean + wallet = await provider.getSigner(1) + wallet2 = await provider.getSigner(2) + wallet3 = await provider.getSigner(3) const allowedAddress = await wallet.getAddress() previousConfiguration = await setupEnvironment( TEST_ENV_CONFIG_FILE, @@ -2160,9 +2163,6 @@ describe('Compute Access Restrictions', () => { provider = new JsonRpcProvider('http://127.0.0.1:8545') publisherAccount = await provider.getSigner(0) - wallet = await provider.getSigner(1) - wallet2 = await provider.getSigner(2) - wallet3 = await provider.getSigner(3) publishedComputeDataset = await publishAsset(computeAsset, publisherAccount) publishedAlgoDataset = await publishAsset(algoAsset, publisherAccount) From 42d95d36f796ea20a84a38f81ed3b2e40b2e60e0 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 14:24:01 +0200 Subject: [PATCH 11/30] remove message from ddo validation --- src/@types/commands.ts | 1 - src/components/httpRoutes/aquarius.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/@types/commands.ts b/src/@types/commands.ts index 3f2636ea0..d6f9e6472 100644 --- a/src/@types/commands.ts +++ b/src/@types/commands.ts @@ -87,7 +87,6 @@ export interface ValidateDDOCommand extends Command { publisherAddress?: string nonce?: string signature?: string - message?: string } export interface StatusCommand extends Command { diff --git a/src/components/httpRoutes/aquarius.ts b/src/components/httpRoutes/aquarius.ts index d440413fa..8b1e7c35e 100644 --- a/src/components/httpRoutes/aquarius.ts +++ b/src/components/httpRoutes/aquarius.ts @@ -180,7 +180,6 @@ aquariusRoutes.post(`${AQUARIUS_API_BASE_PATH}/assets/ddo/validate`, async (req, authorization, nonce, signature, - message: ddo.id + nonce, command: PROTOCOL_COMMANDS.VALIDATE_DDO, caller: req.caller }) From 8f8f0cdd3ace40cb759dd745fb8a0cd0b3077f2f Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 14:24:06 +0200 Subject: [PATCH 12/30] fix --- src/test/integration/compute.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index b9d4da6c9..861d0121f 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -2108,6 +2108,8 @@ describe('Compute Access Restrictions', () => { before(async () => { const artifactsAddresses = getOceanArtifactsAdresses() paymentToken = artifactsAddresses.development.Ocean + provider = new JsonRpcProvider('http://127.0.0.1:8545') + publisherAccount = await provider.getSigner(0) wallet = await provider.getSigner(1) wallet2 = await provider.getSigner(2) wallet3 = await provider.getSigner(3) @@ -2161,9 +2163,6 @@ describe('Compute Access Restrictions', () => { oceanNode.addIndexer(indexer) oceanNode.addC2DEngines() - provider = new JsonRpcProvider('http://127.0.0.1:8545') - publisherAccount = await provider.getSigner(0) - publishedComputeDataset = await publishAsset(computeAsset, publisherAccount) publishedAlgoDataset = await publishAsset(algoAsset, publisherAccount) From 8126570350693c375d337c44071d9144e7b6c14f Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 14:32:56 +0200 Subject: [PATCH 13/30] fix --- src/test/integration/compute.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index 861d0121f..64e054d32 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -2042,7 +2042,7 @@ describe('Compute Access Restrictions', () => { nonce, PROTOCOL_COMMANDS.COMPUTE_START ) - const signature = await signerWallet.signMessage(messageHashBytes) + const signature = await safeSign(signerWallet, messageHashBytes) return { command: PROTOCOL_COMMANDS.COMPUTE_START, @@ -2079,7 +2079,7 @@ describe('Compute Access Restrictions', () => { nonce, PROTOCOL_COMMANDS.FREE_COMPUTE_START ) - const signature = await signerWallet.signMessage(messageHashBytes) + const signature = await safeSign(signerWallet, messageHashBytes) return { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, From 41907ebf5c43339c6ef00e0ccd925c87e9a7bffd Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 14:48:24 +0200 Subject: [PATCH 14/30] add sleep --- src/test/integration/operationsDashboard.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/integration/operationsDashboard.test.ts b/src/test/integration/operationsDashboard.test.ts index 8360d9502..82735e47f 100644 --- a/src/test/integration/operationsDashboard.test.ts +++ b/src/test/integration/operationsDashboard.test.ts @@ -151,7 +151,7 @@ describe('Should test admin operations', () => { const collectFeesHandler: CollectFeesHandler = CoreHandlersRegistry.getInstance( oceanNode ).getHandler(PROTOCOL_COMMANDS.COLLECT_FEES) as CollectFeesHandler - + await sleep(200) const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( wallet.address, From c47c8bd7160a1a0ffda702973b75c9af39d2fd03 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 14:48:36 +0200 Subject: [PATCH 15/30] add more logs --- src/test/unit/download.test.ts | 12 ++++++++++++ src/utils/util.ts | 3 +++ 2 files changed, 15 insertions(+) diff --git a/src/test/unit/download.test.ts b/src/test/unit/download.test.ts index 20a02ff03..b5c3fa020 100644 --- a/src/test/unit/download.test.ts +++ b/src/test/unit/download.test.ts @@ -102,6 +102,12 @@ describe('Should validate files structure for download', () => { signature }) + if (!result.stream) { + throw new Error( + `Encryption failed: ${result.status.error || 'Unknown error'} (HTTP ${result.status.httpStatus})` + ) + } + const encryptedData: string = await streamToString(result.stream as Readable) const serviceData = { files: encryptedData @@ -165,6 +171,12 @@ describe('Should validate files structure for download', () => { signature }) + if (!result.stream) { + throw new Error( + `Encryption failed: ${result.status.error || 'Unknown error'} (HTTP ${result.status.httpStatus})` + ) + } + const encryptedFilesData: string = await streamToString(result.stream as Readable) const sameDDOOtherFiles = ddoObj sameDDOOtherFiles.services[0].files = encryptedFilesData diff --git a/src/utils/util.ts b/src/utils/util.ts index 16c73fb85..3d8f219a8 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -32,6 +32,9 @@ export async function streamToObject(stream: Readable): Promise { } export async function streamToString(stream: Readable) { + if (!stream) { + throw new Error('streamToString: stream is null or undefined') + } const chunks = [] for await (const chunk of stream) { chunks.push(Buffer.from(chunk)) From 2bd3e7c06df43cb3bc5c3ecc3a44ae8d3d0b20a8 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 15:39:39 +0200 Subject: [PATCH 16/30] more fixes --- .../Indexer/processors/BaseProcessor.ts | 4 +- src/components/core/handler/ddoHandler.ts | 6 ++ src/components/httpRoutes/aquarius.ts | 22 ++---- .../integration/operationsDashboard.test.ts | 74 +++++++++---------- 4 files changed, 46 insertions(+), 60 deletions(-) diff --git a/src/components/Indexer/processors/BaseProcessor.ts b/src/components/Indexer/processors/BaseProcessor.ts index 5bde49364..f08e911da 100644 --- a/src/components/Indexer/processors/BaseProcessor.ts +++ b/src/components/Indexer/processors/BaseProcessor.ts @@ -215,7 +215,7 @@ export abstract class BaseEventProcessor { { timeout: 20000 } ) return nonceResponse.status === 200 && nonceResponse.data - ? String(parseInt(nonceResponse.data.nonce) + 1) + ? String(parseFloat(nonceResponse.data.nonce) + 1) : Date.now().toString() } else { return Date.now().toString() @@ -241,7 +241,7 @@ export abstract class BaseEventProcessor { ): Promise { let ddo // Log the flag value - INDEXER_LOGGER.logMessage(`decryptDDO: flag=${flag}`) + INDEXER_LOGGER.debug(`decryptDDO: flag=${flag}`) if ((parseInt(flag) & 2) !== 0) { INDEXER_LOGGER.logMessage( `Decrypting DDO from network: ${this.networkId} created by: ${eventCreator} encrypted by: ${decryptorURL}` diff --git a/src/components/core/handler/ddoHandler.ts b/src/components/core/handler/ddoHandler.ts index 622cbbb5c..97389477d 100644 --- a/src/components/core/handler/ddoHandler.ts +++ b/src/components/core/handler/ddoHandler.ts @@ -747,6 +747,12 @@ export class ValidateDDOHandler extends CommandHandler { if (this.shouldDenyTaskHandling(validationResponse)) { return validationResponse } + if (!task.ddo || !task.ddo.version) { + return { + stream: null, + status: { httpStatus: 400, error: 'Missing DDO version' } + } + } let shouldSign = false const configuration = await getConfiguration() if (configuration.validateUnsignedDDO) { diff --git a/src/components/httpRoutes/aquarius.ts b/src/components/httpRoutes/aquarius.ts index 8b1e7c35e..98c220527 100644 --- a/src/components/httpRoutes/aquarius.ts +++ b/src/components/httpRoutes/aquarius.ts @@ -162,24 +162,12 @@ aquariusRoutes.post(`${AQUARIUS_API_BASE_PATH}/assets/ddo/validate`, async (req, return } - const requestBody = JSON.parse(req.body) - const authorization = req.headers?.authorization - const { publisherAddress, nonce, signature } = requestBody - - // This is for backward compatibility with the old way of sending the DDO - const ddo = requestBody.ddo || JSON.parse(req.body) - - if (!ddo.version) { - res.status(400).send('Missing DDO version') - return - } - const result = await new ValidateDDOHandler(node).handle({ - ddo, - publisherAddress, - authorization, - nonce, - signature, + ddo: req.body.ddo || null, + publisherAddress: (req.body.publisherAddress as string) || null, + signature: (req.body.signature as string) || null, + nonce: (req.body.nonce as string) || null, + authorization: req.headers?.authorization, command: PROTOCOL_COMMANDS.VALIDATE_DDO, caller: req.caller }) diff --git a/src/test/integration/operationsDashboard.test.ts b/src/test/integration/operationsDashboard.test.ts index 82735e47f..b8b09ffdf 100644 --- a/src/test/integration/operationsDashboard.test.ts +++ b/src/test/integration/operationsDashboard.test.ts @@ -152,13 +152,13 @@ describe('Should test admin operations', () => { oceanNode ).getHandler(PROTOCOL_COMMANDS.COLLECT_FEES) as CollectFeesHandler await sleep(200) - const nonce = Date.now().toString() - const messageHashBytes = createHashForSignature( + let nonce = Date.now().toString() + let messageHashBytes = createHashForSignature( wallet.address, nonce, PROTOCOL_COMMANDS.COLLECT_FEES ) - const signature = await wallet.signMessage(messageHashBytes) + let signature = await wallet.signMessage(messageHashBytes) const collectFeesCommand: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, tokenAddress: await getProviderFeeToken(DEVELOPMENT_CHAIN_ID), @@ -169,12 +169,6 @@ describe('Should test admin operations', () => { nonce, signature } - const validationResponse = await collectFeesHandler.validate(collectFeesCommand) - assert(validationResponse, 'invalid collect fees validation response') - assert( - validationResponse.valid === true, - 'validation for collect fees command failed' - ) const providerWallet = wallet const token = new Contract( collectFeesCommand.tokenAddress.toLowerCase(), @@ -182,9 +176,6 @@ describe('Should test admin operations', () => { providerWallet ) const balanceBefore = await token.balanceOf(await destinationWallet.getAddress()) - expect((await collectFeesHandler.validate(collectFeesCommand)).valid).to.be.equal( - true - ) // OK const result = await collectFeesHandler.handle(collectFeesCommand) expect(result.status.httpStatus).to.be.equal(200) // OK @@ -195,15 +186,15 @@ describe('Should test admin operations', () => { expect(await token.balanceOf(await destinationWallet.getAddress())).to.be.equal( balanceBefore + parseUnits(collectFeesCommand.tokenAmount.toString(), 'ether') ) - await sleep(1000) // Test incorrect values for command: node ID and big amount - const nonce2 = Date.now().toString() - const messageHashBytes2 = createHashForSignature( + await sleep(100) + nonce = Date.now().toString() + messageHashBytes = createHashForSignature( wallet.address, nonce, PROTOCOL_COMMANDS.COLLECT_FEES ) - const signature2 = await wallet.signMessage(messageHashBytes2) + signature = await wallet.signMessage(messageHashBytes) const collectFeesCommandWrongNode: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, node: 'My peerID', // dummy peer ID @@ -212,20 +203,20 @@ describe('Should test admin operations', () => { tokenAmount: 0.01, destinationAddress: await wallet.getAddress(), address: wallet.address, - nonce: nonce2, - signature: signature2 + nonce, + signature } expect( (await collectFeesHandler.handle(collectFeesCommandWrongNode)).status.httpStatus ).to.be.equal(400) // NOK - await sleep(1000) - const nonce3 = Date.now().toString() - const messageHashBytes3 = createHashForSignature( + await sleep(100) + nonce = Date.now().toString() + messageHashBytes = createHashForSignature( wallet.address, nonce, PROTOCOL_COMMANDS.COLLECT_FEES ) - const signature3 = await wallet.signMessage(messageHashBytes3) + signature = await wallet.signMessage(messageHashBytes) const collectFeesCommandWrongAmount: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, tokenAddress: getOceanArtifactsAdresses().development.Ocean, @@ -233,8 +224,8 @@ describe('Should test admin operations', () => { tokenAmount: 366666666666, // big amount destinationAddress: await wallet.getAddress(), address: wallet.address, - nonce: nonce3, - signature: signature3 + nonce, + signature } expect( (await collectFeesHandler.handle(collectFeesCommandWrongAmount)).status.httpStatus @@ -275,10 +266,6 @@ describe('Should test admin operations', () => { signature } const reindexTxHandler = new ReindexTxHandler(oceanNode) - const validationResponse = await reindexTxHandler.validate(reindexTxCommand) - assert(validationResponse, 'invalid reindex tx validation response') - assert(validationResponse.valid === true, 'validation for reindex tx command failed') - let reindexResult: any = null INDEXER_CRAWLING_EVENT_EMITTER.addListener( INDEXER_CRAWLING_EVENTS.REINDEX_QUEUE_POP, // triggered when tx completes and removed from queue @@ -410,14 +397,14 @@ describe('Should test admin operations', () => { oceanNode, true ).getHandler(PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD) as IndexingThreadHandler - - const nonce = Date.now().toString() - const messageHashBytes = createHashForSignature( + await sleep(100) + let nonce = Date.now().toString() + let messageHashBytes = createHashForSignature( wallet.address, nonce, PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD ) - const signature = await wallet.signMessage(messageHashBytes) + let signature = await wallet.signMessage(messageHashBytes) const indexingStartCommand: StartStopIndexingCommand = { command: PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD, @@ -427,7 +414,14 @@ describe('Should test admin operations', () => { signature } expect((await indexingHandler.validate(indexingStartCommand)).valid).to.be.equal(true) // OK - await sleep(1000) + await sleep(100) + nonce = Date.now().toString() + messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD + ) + signature = await wallet.signMessage(messageHashBytes) const indexingStopCommand: StartStopIndexingCommand = { command: PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD, action: IndexingCommand.STOP_THREAD, @@ -435,19 +429,17 @@ describe('Should test admin operations', () => { nonce, signature } - expect((await indexingHandler.validate(indexingStopCommand)).valid).to.be.equal(false) // NOK - - // OK now indexingStopCommand.chainId = 8996 - const nonce2 = Date.now().toString() - const messageHashBytes2 = createHashForSignature( + await sleep(100) + nonce = Date.now().toString() + messageHashBytes = createHashForSignature( wallet.address, nonce, PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD ) - const signature2 = await wallet.signMessage(messageHashBytes2) - indexingStopCommand.signature = signature2 - indexingStopCommand.nonce = nonce2 + signature = await wallet.signMessage(messageHashBytes) + indexingStopCommand.signature = signature + indexingStopCommand.nonce = nonce expect((await indexingHandler.validate(indexingStopCommand)).valid).to.be.equal(true) // OK // should exist a running thread for this network atm From 1832b63494363f46e45f132c2363eac70de3c711 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 16:11:44 +0200 Subject: [PATCH 17/30] more fixes --- src/test/integration/operationsDashboard.test.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/test/integration/operationsDashboard.test.ts b/src/test/integration/operationsDashboard.test.ts index b8b09ffdf..48816401b 100644 --- a/src/test/integration/operationsDashboard.test.ts +++ b/src/test/integration/operationsDashboard.test.ts @@ -440,8 +440,6 @@ describe('Should test admin operations', () => { signature = await wallet.signMessage(messageHashBytes) indexingStopCommand.signature = signature indexingStopCommand.nonce = nonce - expect((await indexingHandler.validate(indexingStopCommand)).valid).to.be.equal(true) // OK - // should exist a running thread for this network atm const response = await indexingHandler.handle(indexingStopCommand) console.log({ responseStoppingThread: response }) @@ -452,7 +450,17 @@ describe('Should test admin operations', () => { // restart it again after 5 secs indexingStartCommand.chainId = 8996 + nonce = Date.now().toString() + messageHashBytes = createHashForSignature( + wallet.address, + nonce, + PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD + ) + signature = await wallet.signMessage(messageHashBytes) + indexingStartCommand.signature = signature + indexingStartCommand.nonce = nonce const responseStart = await indexingHandler.handle(indexingStartCommand) + console.log(responseStart) assert(responseStart.stream, 'Failed to get stream when starting thread') expect(responseStart.status.httpStatus).to.be.equal(200) }) From 6c345d27571928e12f40523506060d72d295ea6c Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 19:18:56 +0200 Subject: [PATCH 18/30] more fixes --- src/components/core/utils/nonceHandler.ts | 18 ---- src/test/integration/auth.test.ts | 24 +++-- src/test/integration/compute.test.ts | 21 ----- src/test/integration/configAdmin.test.ts | 39 ++++---- src/test/integration/credentials.test.ts | 50 ++++------- src/test/integration/download.test.ts | 25 ++---- src/test/integration/encryptFile.test.ts | 26 +++--- src/test/integration/nonce.test.ts | 28 +----- .../integration/operationsDashboard.test.ts | 88 +++++++++---------- src/test/unit/commands.test.ts | 13 +-- src/test/unit/download.test.ts | 16 ++-- src/test/unit/indexer/validation.test.ts | 16 ++-- src/test/utils/addresses.ts | 26 ------ src/utils/config/builder.ts | 23 ++--- 14 files changed, 140 insertions(+), 273 deletions(-) delete mode 100644 src/test/utils/addresses.ts diff --git a/src/components/core/utils/nonceHandler.ts b/src/components/core/utils/nonceHandler.ts index 487922254..9f5195153 100644 --- a/src/components/core/utils/nonceHandler.ts +++ b/src/components/core/utils/nonceHandler.ts @@ -266,21 +266,3 @@ export async function isERC1271Valid( return false } } - -export async function sign(message: string, privateKey: string): Promise { - /** Signs a message with a private key - * - * @param message - message to be sign - * @param privateKey - private key from node as Uint8Array - */ - const wallet = new ethers.Wallet('0x' + Buffer.from(privateKey).toString('hex')) - // message to sign - // sign message/nonce - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] - ) - const messageHashBytes = ethers.toBeArray(consumerMessage) - const signature = await wallet.signMessage(messageHashBytes) - return signature -} diff --git a/src/test/integration/auth.test.ts b/src/test/integration/auth.test.ts index eb1da9c24..6fa20e852 100644 --- a/src/test/integration/auth.test.ts +++ b/src/test/integration/auth.test.ts @@ -1,4 +1,4 @@ -import { JsonRpcProvider, Signer, Wallet } from 'ethers' +import { JsonRpcProvider, Signer } from 'ethers' import { Database } from '../../components/database/index.js' import { getConfiguration } from '../../utils/index.js' import { @@ -22,7 +22,7 @@ import { streamToObject } from '../../utils/util.js' import { Readable } from 'stream' import { expect } from 'chai' import { ValidateDDOHandler } from '../../components/core/handler/ddoHandler.js' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('Auth Token Integration Tests', () => { let config: OceanNodeConfig @@ -52,10 +52,7 @@ describe('Auth Token Integration Tests', () => { oceanNode = await OceanNode.getInstance(config, database) provider = new JsonRpcProvider(mockSupportedNetworks['8996'].rpc) - - const consumerPrivateKey = - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - consumerAccount = new Wallet(consumerPrivateKey, provider) + consumerAccount = (await provider.getSigner(1)) as Signer }) after(async () => { @@ -115,18 +112,17 @@ describe('Auth Token Integration Tests', () => { it('should create and validate token', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const consumerAddress = await consumerAccount.getAddress() const nonce = getRandomNonce() const messageHash = createHashForSignature( await consumerAccount.getAddress(), nonce, PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN ) - const signature = await consumerAccount.signMessage(messageHash) + const signature = await safeSign(consumerAccount, messageHash) const handlerResponse = await new CreateAuthTokenHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN, - address: consumerAddress, + address: await consumerAccount.getAddress(), signature, nonce }) @@ -142,11 +138,11 @@ describe('Auth Token Integration Tests', () => { const consumerAddress = await consumerAccount.getAddress() const nonce = getRandomNonce() const messageHash = createHashForSignature( - await consumerAccount.getAddress(), + consumerAddress, nonce, PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN ) - const signature = await consumerAccount.signMessage(messageHash) + const signature = await safeSign(consumerAccount, messageHash) const validUntil = Date.now() + 1000 const handlerResponse = await new CreateAuthTokenHandler(oceanNode).handle({ @@ -171,11 +167,11 @@ describe('Auth Token Integration Tests', () => { const consumerAddress = await consumerAccount.getAddress() const nonce = getRandomNonce() const messageHash = createHashForSignature( - await consumerAccount.getAddress(), + consumerAddress, nonce, PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN ) - const signature = await consumerAccount.signMessage(messageHash) + const signature = await safeSign(consumerAccount, messageHash) const handlerResponse = await new CreateAuthTokenHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN, @@ -240,7 +236,7 @@ describe('Auth Token Integration Tests', () => { nonce, PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN ) - const signature = await consumerAccount.signMessage(messageHash) + const signature = await safeSign(consumerAccount, messageHash) const response2 = await new CreateAuthTokenHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN, diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index 64e054d32..1414d1770 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -110,17 +110,6 @@ describe('Compute', () => { // const now = new Date().getTime() / 1000 const computeJobDuration = 60 * 15 // 15 minutes from now should be enough let firstEnv: ComputeEnvironment - - /* const wallet = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - ) - const wallet2 = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45210' - ) - const wallet3 = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d4521A' - ) */ - // const chainId = DEVELOPMENT_CHAIN_ID const mockSupportedNetworks: RPCS = getMockSupportedNetworks() const chainId = DEVELOPMENT_CHAIN_ID @@ -2018,16 +2007,6 @@ describe('Compute Access Restrictions', () => { let wallet: any let wallet2: any let wallet3: any - /* const wallet = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - ) - const wallet2 = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45210' - ) - const wallet3 = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d4521A' - ) - */ const mockSupportedNetworks: RPCS = getMockSupportedNetworks() const computeJobDuration = 60 * 15 diff --git a/src/test/integration/configAdmin.test.ts b/src/test/integration/configAdmin.test.ts index 7cd498056..1a67667f0 100644 --- a/src/test/integration/configAdmin.test.ts +++ b/src/test/integration/configAdmin.test.ts @@ -1,4 +1,4 @@ -import { Wallet } from 'ethers' +import { Signer, JsonRpcProvider } from 'ethers' import { Database } from '../../components/database/index.js' import { getConfiguration, loadConfigFromFile } from '../../utils/index.js' import { @@ -19,21 +19,22 @@ import { PushConfigHandler } from '../../components/core/admin/pushConfigHandler import { streamToObject } from '../../utils/util.js' import { Readable } from 'stream' import { expect } from 'chai' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('Config Admin Endpoints Integration Tests', () => { let config: OceanNodeConfig let database: Database - let adminAccount: Wallet + let adminAccount: Signer + let nonAdminAccount: Signer let previousConfiguration: OverrideEnvConfig[] let oceanNode: OceanNode const mockSupportedNetworks: RPCS = getMockSupportedNetworks() before(async () => { - const adminPrivateKey = - '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58' - adminAccount = new Wallet(adminPrivateKey) + const provider = new JsonRpcProvider('http://127.0.0.1:8545') + adminAccount = (await provider.getSigner(0)) as Signer + nonAdminAccount = (await provider.getSigner(1)) as Signer const adminAddress = await adminAccount.getAddress() previousConfiguration = await setupEnvironment( @@ -70,7 +71,7 @@ describe('Config Admin Endpoints Integration Tests', () => { nonce, command ) - const signature = await adminAccount.signMessage(messageHashBytes) + const signature = await safeSign(adminAccount, messageHashBytes) return signature } @@ -120,13 +121,13 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should reject fetch config with signature from non-admin', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const nonAdminPrivateKey = - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - const nonAdminAccount = new Wallet(nonAdminPrivateKey) - const expiryTimestamp = Date.now() + 60000 - const message = expiryTimestamp.toString() - const invalidSignature = await nonAdminAccount.signMessage(message) + const messageHashBytes = createHashForSignature( + await nonAdminAccount.getAddress(), + expiryTimestamp.toString(), + PROTOCOL_COMMANDS.FETCH_CONFIG + ) + const invalidSignature = await safeSign(nonAdminAccount, messageHashBytes) const handlerResponse = await new FetchConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.FETCH_CONFIG, @@ -280,13 +281,13 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should reject push config with signature from non-admin', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const nonAdminPrivateKey = - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - const nonAdminAccount = new Wallet(nonAdminPrivateKey) - const expiryTimestamp = Date.now() + 60000 - const message = expiryTimestamp.toString() - const invalidSignature = await nonAdminAccount.signMessage(message) + const messageHashBytes = createHashForSignature( + await nonAdminAccount.getAddress(), + expiryTimestamp.toString(), + PROTOCOL_COMMANDS.FETCH_CONFIG + ) + const invalidSignature = await safeSign(nonAdminAccount, messageHashBytes) const handlerResponse = await new PushConfigHandler(oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, diff --git a/src/test/integration/credentials.test.ts b/src/test/integration/credentials.test.ts index d2ecb0243..db9945ea6 100644 --- a/src/test/integration/credentials.test.ts +++ b/src/test/integration/credentials.test.ts @@ -14,7 +14,7 @@ * 5. Try to Download the asset by all consumers. */ import { expect, assert } from 'chai' -import { JsonRpcProvider, Signer, ethers, Contract, EventLog } from 'ethers' +import { JsonRpcProvider, Signer, Contract, EventLog } from 'ethers' import { Database } from '../../components/database/index.js' import { OceanIndexer } from '../../components/Indexer/index.js' import { OceanNode } from '../../OceanNode.js' @@ -57,7 +57,6 @@ import { downloadAssetWithCredentials, downloadAssetWithCredentialsWithMatchAll } from '../data/assets.js' -import { ganachePrivateKeys } from '../utils/addresses.js' import { homedir } from 'os' import AccessListFactory from '@oceanprotocol/contracts/artifacts/contracts/accesslists/AccessListFactory.sol/AccessListFactory.json' with { type: 'json' } import AccessList from '@oceanprotocol/contracts/artifacts/contracts/accesslists/AccessList.sol/AccessList.json' with { type: 'json' } @@ -66,7 +65,7 @@ import { ComputeInitializeHandler } from '../../components/core/compute/initiali import { ComputeAlgorithm, ComputeAsset } from '../../@types/index.js' import { ComputeGetEnvironmentsHandler } from '../../components/core/compute/environments.js' import { ComputeInitializeCommand } from '../../@types/commands.js' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('[Credentials Flow] - Should run a complete node flow.', () => { let config: OceanNodeConfig @@ -123,11 +122,9 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { JSON.stringify(mockSupportedNetworks), JSON.stringify([8996]), '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58', - JSON.stringify(['0xe2DD09d719Da89e5a3D0F2549c7E24566e947260']), - JSON.stringify(['0xe2DD09d719Da89e5a3D0F2549c7E24566e947260']), - JSON.stringify([ - await publisherAccount.getAddress() // signer 0 - ]), + JSON.stringify([await publisherAccount.getAddress()]), + JSON.stringify([await publisherAccount.getAddress()]), + JSON.stringify([await publisherAccount.getAddress()]), `${homedir}/.ocean/ocean-contracts/artifacts/address.json`, '[{"socketPath":"/var/run/docker.sock","resources":[{"id":"disk","total":10}],"storageExpiry":604800,"maxJobDuration":3600,"minJobDuration":60,"fees":{"' + DEVELOPMENT_CHAIN_ID + @@ -332,18 +329,15 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { this.timeout(DEFAULT_TEST_TIMEOUT * 3) const doCheck = async () => { - const consumerAddress = consumerAddresses[0] - const consumerPrivateKey = ganachePrivateKeys[consumerAddress] const transferTxId = orderTxIdsWithMatchAll[0] - const wallet = new ethers.Wallet(consumerPrivateKey) const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await consumerAccounts[0].getAddress(), nonce, PROTOCOL_COMMANDS.DOWNLOAD ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccounts[0], messageHashBytes) const downloadTask = { fileIndex: 0, @@ -351,7 +345,7 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { serviceId: ddoWithMatchAll.services[0].id, transferTxId, nonce, - consumerAddress, + consumerAddress: await consumerAccounts[0].getAddress(), signature, command: PROTOCOL_COMMANDS.DOWNLOAD } @@ -370,18 +364,14 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { this.timeout(DEFAULT_TEST_TIMEOUT * 3) const doCheck = async () => { - const consumerAddress = consumerAddresses[0] - const consumerPrivateKey = ganachePrivateKeys[consumerAddress] const transferTxId = orderTxIds[0] - - const wallet = new ethers.Wallet(consumerPrivateKey) const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await consumerAccounts[0].getAddress(), nonce, PROTOCOL_COMMANDS.DOWNLOAD ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccounts[0], messageHashBytes) const downloadTask = { fileIndex: 0, @@ -389,7 +379,7 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { serviceId: ddo.services[0].id, transferTxId, nonce, - consumerAddress, + consumerAddress: await consumerAccounts[0].getAddress(), signature, command: PROTOCOL_COMMANDS.DOWNLOAD } @@ -520,18 +510,15 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { this.timeout(DEFAULT_TEST_TIMEOUT * 3) const doCheck = async () => { - const consumerAddress = consumerAddresses[1] - const consumerPrivateKey = ganachePrivateKeys[consumerAddress] const transferTxId = orderTxIds[1] - const wallet = new ethers.Wallet(consumerPrivateKey) const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await consumerAccounts[1].getAddress(), nonce, PROTOCOL_COMMANDS.DOWNLOAD ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccounts[1], messageHashBytes) const downloadTask = { fileIndex: 0, @@ -539,7 +526,7 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { serviceId: ddo.services[0].id, transferTxId, nonce, - consumerAddress, + consumerAddress: await consumerAccounts[1].getAddress(), signature, command: PROTOCOL_COMMANDS.DOWNLOAD } @@ -560,18 +547,15 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { this.timeout(DEFAULT_TEST_TIMEOUT * 3) const doCheck = async () => { - const consumerAddress = consumerAddresses[2] - const consumerPrivateKey = ganachePrivateKeys[consumerAddress] const transferTxId = orderTxIds[1] - const wallet = new ethers.Wallet(consumerPrivateKey) const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await consumerAccounts[2].getAddress(), nonce, PROTOCOL_COMMANDS.DOWNLOAD ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccounts[2], messageHashBytes) const downloadTask = { fileIndex: 0, @@ -579,7 +563,7 @@ describe('[Credentials Flow] - Should run a complete node flow.', () => { serviceId: ddo.services[0].id, transferTxId, nonce, - consumerAddress, + consumerAddress: await consumerAccounts[2].getAddress(), signature, command: PROTOCOL_COMMANDS.DOWNLOAD } diff --git a/src/test/integration/download.test.ts b/src/test/integration/download.test.ts index 861824a9c..e02b041a8 100644 --- a/src/test/integration/download.test.ts +++ b/src/test/integration/download.test.ts @@ -1,5 +1,5 @@ import { expect, assert } from 'chai' -import { JsonRpcProvider, Signer, ethers } from 'ethers' +import { JsonRpcProvider, Signer } from 'ethers' import { Database } from '../../components/database/index.js' import { OceanIndexer } from '../../components/Indexer/index.js' import { OceanNode } from '../../OceanNode.js' @@ -43,7 +43,7 @@ import { publishAsset, orderAsset } from '../utils/assets.js' import { downloadAsset } from '../data/assets.js' import { genericDDO } from '../data/ddo.js' import { homedir } from 'os' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('[Download Flow] - Should run a complete node flow.', () => { let config: OceanNodeConfig @@ -52,12 +52,11 @@ describe('[Download Flow] - Should run a complete node flow.', () => { let provider: JsonRpcProvider let publisherAccount: Signer let consumerAccount: Signer - let consumerAddress: string let orderTxId: string let publishedDataset: any let actualDDO: any let indexer: OceanIndexer - let anotherConsumer: ethers.Wallet + let anotherConsumer: Signer const mockSupportedNetworks: RPCS = getMockSupportedNetworks() const serviceId = '0' @@ -104,14 +103,10 @@ describe('[Download Flow] - Should run a complete node flow.', () => { } provider = new JsonRpcProvider('http://127.0.0.1:8545') - anotherConsumer = new ethers.Wallet( - ENVIRONMENT_VARIABLES.NODE2_PRIVATE_KEY.value, - provider - ) publisherAccount = (await provider.getSigner(0)) as Signer consumerAccount = (await provider.getSigner(1)) as Signer - consumerAddress = await consumerAccount.getAddress() + anotherConsumer = (await provider.getSigner(2)) as Signer }) it('should get node status', async () => { @@ -244,23 +239,21 @@ describe('[Download Flow] - Should run a complete node flow.', () => { this.timeout(DEFAULT_TEST_TIMEOUT * 3) const doCheck = async () => { - const wallet = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - ) const nonce = Date.now().toString() + const messageHashBytes = createHashForSignature( - wallet.address, + await consumerAccount.getAddress(), nonce, PROTOCOL_COMMANDS.DOWNLOAD ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const downloadTask = { fileIndex: 0, documentId: publishedDataset.ddo.id, serviceId: publishedDataset.ddo.services[0].id, transferTxId: orderTxId, nonce, - consumerAddress, + consumerAddress: await consumerAccount.getAddress(), signature, command: PROTOCOL_COMMANDS.DOWNLOAD } @@ -299,7 +292,7 @@ describe('[Download Flow] - Should run a complete node flow.', () => { nonce, PROTOCOL_COMMANDS.DOWNLOAD ) - const signature = await anotherConsumer.signMessage(messageHashBytes) + const signature = await safeSign(anotherConsumer, messageHashBytes) const doCheck = async () => { const downloadTask = { diff --git a/src/test/integration/encryptFile.test.ts b/src/test/integration/encryptFile.test.ts index 685c162b1..d65d10015 100644 --- a/src/test/integration/encryptFile.test.ts +++ b/src/test/integration/encryptFile.test.ts @@ -7,7 +7,7 @@ import { Readable } from 'stream' import { EncryptFileHandler } from '../../components/core/handler/encryptHandler.js' import { EncryptFileCommand } from '../../@types/commands' import { EncryptMethod, FileObjectType, UrlFileObject } from '../../@types/fileObject.js' -import { Wallet, ethers } from 'ethers' +import { Signer, JsonRpcProvider } from 'ethers' import fs from 'fs' import { OverrideEnvConfig, @@ -17,13 +17,13 @@ import { tearDownEnvironment } from '../utils/utils.js' import { Database } from '../../components/database/index.js' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('Encrypt File', () => { let config: OceanNodeConfig let oceanNode: OceanNode let previousConfiguration: OverrideEnvConfig[] - let anotherConsumerWallet: Wallet + let anotherConsumerWallet: Signer before(async () => { previousConfiguration = await setupEnvironment( @@ -36,26 +36,22 @@ describe('Encrypt File', () => { config = await getConfiguration(true) // Force reload the configuration const dbconn = await Database.init(config.dbConfig) oceanNode = await OceanNode.getInstance(config, dbconn) - anotherConsumerWallet = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - ) + const provider = new JsonRpcProvider('http://127.0.0.1:8545') + anotherConsumerWallet = (await provider.getSigner(1)) as Signer }) it('should encrypt files', async () => { - const wallet = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - ) const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await anotherConsumerWallet.getAddress(), nonce, PROTOCOL_COMMANDS.ENCRYPT_FILE ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(anotherConsumerWallet, messageHashBytes) const encryptFileTask: EncryptFileCommand = { command: PROTOCOL_COMMANDS.ENCRYPT_FILE, nonce, - consumerAddress: await wallet.getAddress(), + consumerAddress: await anotherConsumerWallet.getAddress(), signature, encryptionType: EncryptMethod.AES, files: { @@ -88,7 +84,7 @@ describe('Encrypt File', () => { nonce, PROTOCOL_COMMANDS.ENCRYPT_FILE ) - const signature = await anotherConsumerWallet.signMessage(messageHashBytes) + const signature = await safeSign(anotherConsumerWallet, messageHashBytes) const encryptFileTask: EncryptFileCommand = { command: PROTOCOL_COMMANDS.ENCRYPT_FILE, encryptionType: EncryptMethod.AES, @@ -120,7 +116,7 @@ describe('Encrypt File', () => { nonce, PROTOCOL_COMMANDS.ENCRYPT_FILE ) - const signature = await anotherConsumerWallet.signMessage(messageHashBytes) + const signature = await safeSign(anotherConsumerWallet, messageHashBytes) const encryptFileTask: EncryptFileCommand = { command: PROTOCOL_COMMANDS.ENCRYPT_FILE, encryptionType: EncryptMethod.ECIES, @@ -150,7 +146,7 @@ describe('Encrypt File', () => { nonce, PROTOCOL_COMMANDS.ENCRYPT_FILE ) - const signature = await anotherConsumerWallet.signMessage(messageHashBytes) + const signature = await safeSign(anotherConsumerWallet, messageHashBytes) const encryptFileTask: EncryptFileCommand = { command: PROTOCOL_COMMANDS.ENCRYPT_FILE, encryptionType: EncryptMethod.AES, diff --git a/src/test/integration/nonce.test.ts b/src/test/integration/nonce.test.ts index f1030ba72..be45f9f3e 100644 --- a/src/test/integration/nonce.test.ts +++ b/src/test/integration/nonce.test.ts @@ -1,5 +1,5 @@ import { expect, assert } from 'chai' -import { ethers, ZeroAddress } from 'ethers' +import { ZeroAddress } from 'ethers' import { nonceSchema } from '../data/nonceSchema.js' import { Typesense, convertTypesenseConfig } from '../../components/database/typesense.js' @@ -29,32 +29,6 @@ describe('handle nonce', () => { assert(result.num_documents >= 0, 'num_documents is not a valid number') }) - it('should validate signature', async () => { - try { - await typesense - .collections(nonceSchema.name) - .documents() - .retrieve('0x4cc9DBfc4bEeA8c986c61DAABB350C2eC55e29d1') - // if not, create it now - } catch (ex) { - await typesense.collections(nonceSchema.name).documents().create({ - id: '0x4cc9DBfc4bEeA8c986c61DAABB350C2eC55e29d1', - nonce: 1 - }) - } - const wallet = new ethers.Wallet( - '0xbee525d70c715bee6ca15ea5113e544d13cc1bb2817e07113d0af7755ddb6391' - ) - // message to sign - const nonce = '1' - const expectedAddress = await wallet.getAddress() - // '0x8F292046bb73595A978F4e7A131b4EBd03A15e8a' - // sign message/nonce - const signature = await wallet.signMessage(nonce) - const actualAddress = ethers.verifyMessage(nonce, signature) - expect(actualAddress).to.be.equal(expectedAddress) - }) - it('should get nonce (1)', async () => { const document = await typesense .collections(nonceSchema.name) diff --git a/src/test/integration/operationsDashboard.test.ts b/src/test/integration/operationsDashboard.test.ts index 48816401b..c9c492963 100644 --- a/src/test/integration/operationsDashboard.test.ts +++ b/src/test/integration/operationsDashboard.test.ts @@ -1,6 +1,6 @@ import { assert, expect } from 'chai' import { Readable } from 'stream' -import { Signer, JsonRpcProvider, ethers, Contract, parseUnits } from 'ethers' +import { Signer, JsonRpcProvider, Contract, parseUnits } from 'ethers' import { Database } from '../../components/database/index.js' import { OceanNode } from '../../OceanNode.js' import { RPCS } from '../../@types/blockchain.js' @@ -55,7 +55,7 @@ import { CollectFeesHandler } from '../../components/core/admin/collectFeesHandl import { getProviderFeeToken } from '../../components/core/utils/feesHandler.js' import { KeyManager } from '../../components/KeyManager/index.js' import { BlockchainRegistry } from '../../components/BlockchainRegistry/index.js' -import { createHashForSignature } from '../utils/signature.js' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('Should test admin operations', () => { let config: OceanNodeConfig @@ -63,22 +63,18 @@ describe('Should test admin operations', () => { let publishedDataset: any let dbconn: Database let indexer: OceanIndexer - const provider = new JsonRpcProvider('http://127.0.0.1:8545') - const wallet = new ethers.Wallet( - '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58', - provider - ) - const destinationWallet = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209', - provider - ) - + let adminWallet: Signer + let destinationWallet: Signer const mockSupportedNetworks: RPCS = getMockSupportedNetworks() let previousConfiguration: OverrideEnvConfig[] before(async () => { // override and save configuration (always before calling getConfig()) + const provider = new JsonRpcProvider('http://127.0.0.1:8545') + adminWallet = (await provider.getSigner(0)) as Signer + destinationWallet = (await provider.getSigner(1)) as Signer + previousConfiguration = await setupEnvironment( TEST_ENV_CONFIG_FILE, buildEnvOverrideConfig( @@ -95,7 +91,7 @@ describe('Should test admin operations', () => { JSON.stringify([8996]), '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58', JSON.stringify(['0xe2DD09d719Da89e5a3D0F2549c7E24566e947260']), - JSON.stringify([await wallet.getAddress()]), + JSON.stringify([await adminWallet.getAddress()]), `${homedir}/.ocean/ocean-contracts/artifacts/address.json` ] ) @@ -125,16 +121,16 @@ describe('Should test admin operations', () => { it('validation should pass for stop node command', async () => { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.STOP_NODE ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(adminWallet, messageHashBytes) const stopNodeCommand: AdminStopNodeCommand = { command: PROTOCOL_COMMANDS.STOP_NODE, node: oceanNode.getKeyManager().getPeerId().toString(), nonce, - address: wallet.address, + address: await adminWallet.getAddress(), signature } const validationResponse = await new StopNodeHandler(oceanNode).validate( @@ -154,26 +150,25 @@ describe('Should test admin operations', () => { await sleep(200) let nonce = Date.now().toString() let messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.COLLECT_FEES ) - let signature = await wallet.signMessage(messageHashBytes) + let signature = await safeSign(adminWallet, messageHashBytes) const collectFeesCommand: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, tokenAddress: await getProviderFeeToken(DEVELOPMENT_CHAIN_ID), chainId: DEVELOPMENT_CHAIN_ID, tokenAmount: 0.01, destinationAddress: await destinationWallet.getAddress(), - address: wallet.address, + address: await adminWallet.getAddress(), nonce, signature } - const providerWallet = wallet const token = new Contract( collectFeesCommand.tokenAddress.toLowerCase(), ERC20Template.abi, - providerWallet + adminWallet ) const balanceBefore = await token.balanceOf(await destinationWallet.getAddress()) const result = await collectFeesHandler.handle(collectFeesCommand) @@ -190,19 +185,19 @@ describe('Should test admin operations', () => { await sleep(100) nonce = Date.now().toString() messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.COLLECT_FEES ) - signature = await wallet.signMessage(messageHashBytes) + signature = await safeSign(adminWallet, messageHashBytes) const collectFeesCommandWrongNode: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, node: 'My peerID', // dummy peer ID tokenAddress: getOceanArtifactsAdresses().development.Ocean, chainId: DEVELOPMENT_CHAIN_ID, tokenAmount: 0.01, - destinationAddress: await wallet.getAddress(), - address: wallet.address, + destinationAddress: await adminWallet.getAddress(), + address: await adminWallet.getAddress(), nonce, signature } @@ -212,18 +207,18 @@ describe('Should test admin operations', () => { await sleep(100) nonce = Date.now().toString() messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.COLLECT_FEES ) - signature = await wallet.signMessage(messageHashBytes) + signature = await safeSign(adminWallet, messageHashBytes) const collectFeesCommandWrongAmount: AdminCollectFeesCommand = { command: PROTOCOL_COMMANDS.COLLECT_FEES, tokenAddress: getOceanArtifactsAdresses().development.Ocean, chainId: DEVELOPMENT_CHAIN_ID, tokenAmount: 366666666666, // big amount - destinationAddress: await wallet.getAddress(), - address: wallet.address, + destinationAddress: await adminWallet.getAddress(), + address: await adminWallet.getAddress(), nonce, signature } @@ -234,7 +229,7 @@ describe('Should test admin operations', () => { it('should publish dataset', async function () { this.timeout(DEFAULT_TEST_TIMEOUT * 2) - publishedDataset = await publishAsset(downloadAsset, wallet as Signer) + publishedDataset = await publishAsset(downloadAsset, adminWallet) const { ddo, wasTimeout } = await waitToIndex( publishedDataset.ddo.id, EVENTS.METADATA_CREATED, @@ -250,18 +245,18 @@ describe('Should test admin operations', () => { await waitToIndex(publishedDataset.ddo.did, EVENTS.METADATA_CREATED) const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.REINDEX_TX ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(adminWallet, messageHashBytes) const reindexTxCommand: AdminReindexTxCommand = { command: PROTOCOL_COMMANDS.REINDEX_TX, node: oceanNode.getKeyManager().getPeerId().toString(), txId: publishedDataset.trxReceipt.hash, chainId: DEVELOPMENT_CHAIN_ID, - address: wallet.address, + address: await adminWallet.getAddress(), nonce, signature } @@ -329,16 +324,16 @@ describe('Should test admin operations', () => { } else { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.REINDEX_CHAIN ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(adminWallet, messageHashBytes) const reindexChainCommand: AdminReindexChainCommand = { command: PROTOCOL_COMMANDS.REINDEX_CHAIN, node: oceanNode.getKeyManager().getPeerId().toString(), chainId: DEVELOPMENT_CHAIN_ID, - address: wallet.address, + address: await adminWallet.getAddress(), nonce, signature } @@ -400,16 +395,16 @@ describe('Should test admin operations', () => { await sleep(100) let nonce = Date.now().toString() let messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD ) - let signature = await wallet.signMessage(messageHashBytes) + let signature = await safeSign(adminWallet, messageHashBytes) const indexingStartCommand: StartStopIndexingCommand = { command: PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD, action: IndexingCommand.START_THREAD, - address: wallet.address, + address: await adminWallet.getAddress(), nonce, signature } @@ -417,15 +412,15 @@ describe('Should test admin operations', () => { await sleep(100) nonce = Date.now().toString() messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD ) - signature = await wallet.signMessage(messageHashBytes) + signature = await safeSign(adminWallet, messageHashBytes) const indexingStopCommand: StartStopIndexingCommand = { command: PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD, action: IndexingCommand.STOP_THREAD, - address: wallet.address, + address: await adminWallet.getAddress(), nonce, signature } @@ -433,11 +428,11 @@ describe('Should test admin operations', () => { await sleep(100) nonce = Date.now().toString() messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD ) - signature = await wallet.signMessage(messageHashBytes) + signature = await safeSign(adminWallet, messageHashBytes) indexingStopCommand.signature = signature indexingStopCommand.nonce = nonce // should exist a running thread for this network atm @@ -452,15 +447,14 @@ describe('Should test admin operations', () => { indexingStartCommand.chainId = 8996 nonce = Date.now().toString() messageHashBytes = createHashForSignature( - wallet.address, + await adminWallet.getAddress(), nonce, PROTOCOL_COMMANDS.HANDLE_INDEXING_THREAD ) - signature = await wallet.signMessage(messageHashBytes) + signature = await safeSign(adminWallet, messageHashBytes) indexingStartCommand.signature = signature indexingStartCommand.nonce = nonce const responseStart = await indexingHandler.handle(indexingStartCommand) - console.log(responseStart) assert(responseStart.stream, 'Failed to get stream when starting thread') expect(responseStart.status.httpStatus).to.be.equal(200) }) diff --git a/src/test/unit/commands.test.ts b/src/test/unit/commands.test.ts index 1c2298b90..4d967928f 100644 --- a/src/test/unit/commands.test.ts +++ b/src/test/unit/commands.test.ts @@ -53,18 +53,19 @@ import { ReindexTxHandler } from '../../components/core/admin/reindexTxHandler.j import { ReindexChainHandler } from '../../components/core/admin/reindexChainHandler.js' import { CollectFeesHandler } from '../../components/core/admin/collectFeesHandler.js' import { GetJobsHandler } from '../../components/core/handler/getJobs.js' -import { Wallet } from 'ethers' -import { createHashForSignature } from '../utils/signature.js' +import { Signer, JsonRpcProvider } from 'ethers' +import { createHashForSignature, safeSign } from '../utils/signature.js' describe('Commands and handlers', () => { let node: OceanNode - let consumerAccount: Wallet + let consumerAccount: Signer let consumerAddress: string before(async () => { const config = await getConfiguration() const keyManager = new KeyManager(config) const db = await Database.init(config.dbConfig) node = OceanNode.getInstance(config, db, null, null, null, keyManager, null, true) - consumerAccount = new Wallet(process.env.PRIVATE_KEY) + const provider = new JsonRpcProvider('http://127.0.0.1:8545') + consumerAccount = (await provider.getSigner(0)) as Signer consumerAddress = await consumerAccount.getAddress() }) @@ -144,7 +145,7 @@ describe('Commands and handlers', () => { nonce, PROTOCOL_COMMANDS.ENCRYPT ) - let signature = await consumerAccount.signMessage(messageHashBytes) + let signature = await safeSign(consumerAccount, messageHashBytes) const encryptCommand: EncryptCommand = { blob: '1425252525', command: PROTOCOL_COMMANDS.ENCRYPT, @@ -166,7 +167,7 @@ describe('Commands and handlers', () => { nonce, PROTOCOL_COMMANDS.ENCRYPT_FILE ) - signature = await consumerAccount.signMessage(messageHashBytes2) + signature = await safeSign(consumerAccount, messageHashBytes2) const encryptFileCommand: EncryptFileCommand = { rawData: Buffer.from('12345'), command: PROTOCOL_COMMANDS.ENCRYPT_FILE, diff --git a/src/test/unit/download.test.ts b/src/test/unit/download.test.ts index b5c3fa020..78249afb0 100644 --- a/src/test/unit/download.test.ts +++ b/src/test/unit/download.test.ts @@ -19,8 +19,8 @@ import { validateFilesStructure } from '../../components/core/handler/downloadHa import { AssetUtils, isConfidentialChainDDO } from '../../utils/asset.js' import { DEVELOPMENT_CHAIN_ID, KNOWN_CONFIDENTIAL_EVMS } from '../../utils/address.js' import { DDO } from '@oceanprotocol/ddo-js' -import { Wallet } from 'ethers' -import { createHashForSignature } from '../utils/signature.js' +import { Signer, JsonRpcProvider } from 'ethers' +import { createHashForSignature, safeSign } from '../utils/signature.js' let envOverrides: OverrideEnvConfig[] let config: OceanNodeConfig @@ -28,17 +28,19 @@ let db: Database let oceanNode: OceanNode describe('Should validate files structure for download', () => { - let consumerAccount: Wallet + let consumerAccount: Signer before(async () => { envOverrides = buildEnvOverrideConfig( [ENVIRONMENT_VARIABLES.PRIVATE_KEY], ['0x3634cc4a3d2694a1186a7ce545f149e022eea103cc254d18d08675104bb4b5ac'] ) envOverrides = await setupEnvironment(TEST_ENV_CONFIG_FILE, envOverrides) + config = await getConfiguration(true) db = await Database.init(config.dbConfig) oceanNode = OceanNode.getInstance(config, db) - consumerAccount = new Wallet(process.env.PRIVATE_KEY) + const provider = new JsonRpcProvider('http://127.0.0.1:8545') + consumerAccount = (await provider.getSigner(0)) as Signer }) const ddoObj: DDO = { @@ -87,11 +89,11 @@ describe('Should validate files structure for download', () => { const getDecryptedData = async function () { const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - consumerAccount.address, + await consumerAccount.getAddress(), nonce, PROTOCOL_COMMANDS.ENCRYPT ) - const signature = await consumerAccount.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const result = await new EncryptHandler(oceanNode).handle({ blob: JSON.stringify(assetURL), encoding: 'string', @@ -160,7 +162,7 @@ describe('Should validate files structure for download', () => { nonce, PROTOCOL_COMMANDS.ENCRYPT ) - const signature = await consumerAccount.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const result = await new EncryptHandler(oceanNode).handle({ blob: JSON.stringify(newAssetURL), encoding: 'string', diff --git a/src/test/unit/indexer/validation.test.ts b/src/test/unit/indexer/validation.test.ts index 27cd526f3..a59feb0e0 100644 --- a/src/test/unit/indexer/validation.test.ts +++ b/src/test/unit/indexer/validation.test.ts @@ -18,9 +18,9 @@ import { RPCS } from '../../../@types/blockchain.js' import { Database } from '../../../components/database/index.js' import { OceanNodeConfig } from '../../../@types/OceanNode.js' // import sinon, { SinonSandbox } from 'sinon' -import { ethers } from 'ethers' +import { Signer, JsonRpcProvider } from 'ethers' import { Readable } from 'stream' -import { createHashForSignature } from '../../utils/signature.js' +import { createHashForSignature, safeSign } from '../../utils/signature.js' describe('Schema validation tests', () => { const mockSupportedNetworks: RPCS = getMockSupportedNetworks() @@ -29,6 +29,7 @@ describe('Schema validation tests', () => { let mockDatabase: Database let config: OceanNodeConfig let oceanNode: OceanNode + let consumerAccount: Signer // let sandbox: SinonSandbox // For token validation, please check integration test cases @@ -51,6 +52,8 @@ describe('Schema validation tests', () => { ) envOverrides = await setupEnvironment(TEST_ENV_CONFIG_FILE, envOverrides) config = await getConfiguration(true) + const provider = new JsonRpcProvider('http://127.0.0.1:8545') + consumerAccount = (await provider.getSigner(1)) as Signer /* sandbox = sinon.createSandbox() sandbox.stub(Database, 'init').resolves({ nonce: {}, @@ -179,19 +182,16 @@ describe('Schema validation tests', () => { const ddo: DDO = { ...(ddoInstance.getDDOData() as DDO) } - const wallet = new ethers.Wallet( - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209' - ) const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( - await wallet.address, + await consumerAccount.getAddress(), nonce, PROTOCOL_COMMANDS.VALIDATE_DDO ) - const signature = await wallet.signMessage(messageHashBytes) + const signature = await safeSign(consumerAccount, messageHashBytes) const task = { ddo, - publisherAddress: await wallet.getAddress(), + publisherAddress: await consumerAccount.getAddress(), nonce, signature, command: PROTOCOL_COMMANDS.VALIDATE_DDO diff --git a/src/test/utils/addresses.ts b/src/test/utils/addresses.ts deleted file mode 100644 index 7afcad56d..000000000 --- a/src/test/utils/addresses.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Addresses for testing available from ganache - */ - -export const ganachePrivateKeys: Record = { - '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260': - '0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58', - '0xBE5449a6A97aD46c8558A3356267Ee5D2731ab5e': - '0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209', - '0xA78deb2Fa79463945C247991075E2a0e98Ba7A09': - '0x5d75837394b078ce97bc289fa8d75e21000573520bfa7784a9d28ccaae602bf8', - '0x02354A1F160A3fd7ac8b02ee91F04104440B28E7': - '0x8467415bb2ba7c91084d932276214b11a3dd9bdb2930fefa194b666dd8020b99', - '0xe17D2A07EFD5b112F4d675ea2d122ddb145d117B': - '0x1f990f8b013fc5c7955e0f8746f11ded231721b9cf3f99ff06cdc03492b28090', - '0xA32C84D2B44C041F3a56afC07a33f8AC5BF1A071': - '0x732fbb7c355aa8898f4cff92fa7a6a947339eaf026a08a51f171199e35a18ae0', - '0xFF3fE9eb218EAe9ae1eF9cC6C4db238B770B65CC': - '0x8683d6511213ac949e093ca8e9179514d4c56ce5ea9b83068f723593f913b1ab', - '0x529043886F21D9bc1AE0feDb751e34265a246e47': - '0x1d751ded5a32226054cd2e71261039b65afb9ee1c746d055dd699b1150a5befc', - '0xe08A1dAe983BC701D05E492DB80e0144f8f4b909': - '0xfd5c1ccea015b6d663618850824154a3b3fb2882c46cefb05b9a93fea8c3d215', - '0xbcE5A3468386C64507D30136685A99cFD5603135': - '0x1263dc73bef43a9da06149c7e598f52025bf4027f1d6c13896b71e81bb9233fb' -} diff --git a/src/utils/config/builder.ts b/src/utils/config/builder.ts index 1c56bce04..a89d0ac2e 100644 --- a/src/utils/config/builder.ts +++ b/src/utils/config/builder.ts @@ -1,15 +1,17 @@ -import type { OceanNodeConfig, OceanNodeKeys } from '../../@types/OceanNode.js' +import type { OceanNodeConfig } from '../../@types/OceanNode.js' import type { C2DClusterInfo, C2DDockerConfig } from '../../@types/C2D/C2D.js' import type { RPCS } from '../../@types/blockchain.js' import type { FeeTokens } from '../../@types/Fees.js' import { C2DClusterType } from '../../@types/C2D/C2D.js' -import { privateKeyFromRaw } from '@libp2p/crypto/keys' -import { peerIdFromPrivateKey } from '@libp2p/peer-id' -import { Wallet } from 'ethers' +// import { privateKeyFromRaw } from '@libp2p/crypto/keys' +// import { peerIdFromPrivateKey } from '@libp2p/peer-id' +// import { Wallet } from 'ethers' import fs from 'fs' import os from 'os' import path from 'path' -import { hexStringToByteArray, computeCodebaseHash } from '../index.js' +// import { hexStringToByteArray, computeCodebaseHash } from '../index.js' +import { computeCodebaseHash } from '../index.js' + import { getOceanArtifactsAdresses, OCEAN_ARTIFACTS_ADDRESSES_PER_CHAIN @@ -84,17 +86,6 @@ function preprocessConfigData(data: any): void { } } -export function getPeerIdFromPrivateKey(privateKey: string): OceanNodeKeys { - const key = privateKeyFromRaw(hexStringToByteArray(privateKey.slice(2))) - - return { - peerId: peerIdFromPrivateKey(key), - publicKey: key.publicKey.raw, - privateKey: key, - ethAddress: new Wallet(privateKey.substring(2)).address - } -} - export function getDefaultFeeTokens(supportedNetworks?: RPCS): FeeTokens[] { const nodeFeesTokens: FeeTokens[] = [] let addressesData: any = getOceanArtifactsAdresses() From 7b9d2bb4db19031fe128d4f5fb3bbfec27bd0760 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 19:50:29 +0200 Subject: [PATCH 19/30] fixes --- src/test/unit/commands.test.ts | 7 +++---- src/test/unit/download.test.ts | 7 +++---- src/test/unit/indexer/validation.test.ts | 7 +++---- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/test/unit/commands.test.ts b/src/test/unit/commands.test.ts index 4d967928f..0b98aa3c0 100644 --- a/src/test/unit/commands.test.ts +++ b/src/test/unit/commands.test.ts @@ -53,19 +53,18 @@ import { ReindexTxHandler } from '../../components/core/admin/reindexTxHandler.j import { ReindexChainHandler } from '../../components/core/admin/reindexChainHandler.js' import { CollectFeesHandler } from '../../components/core/admin/collectFeesHandler.js' import { GetJobsHandler } from '../../components/core/handler/getJobs.js' -import { Signer, JsonRpcProvider } from 'ethers' +import { Wallet } from 'ethers' import { createHashForSignature, safeSign } from '../utils/signature.js' describe('Commands and handlers', () => { let node: OceanNode - let consumerAccount: Signer + let consumerAccount: Wallet let consumerAddress: string before(async () => { const config = await getConfiguration() const keyManager = new KeyManager(config) const db = await Database.init(config.dbConfig) node = OceanNode.getInstance(config, db, null, null, null, keyManager, null, true) - const provider = new JsonRpcProvider('http://127.0.0.1:8545') - consumerAccount = (await provider.getSigner(0)) as Signer + consumerAccount = new Wallet(process.env.PRIVATE_KEY) consumerAddress = await consumerAccount.getAddress() }) diff --git a/src/test/unit/download.test.ts b/src/test/unit/download.test.ts index 78249afb0..4fc8779a3 100644 --- a/src/test/unit/download.test.ts +++ b/src/test/unit/download.test.ts @@ -19,7 +19,7 @@ import { validateFilesStructure } from '../../components/core/handler/downloadHa import { AssetUtils, isConfidentialChainDDO } from '../../utils/asset.js' import { DEVELOPMENT_CHAIN_ID, KNOWN_CONFIDENTIAL_EVMS } from '../../utils/address.js' import { DDO } from '@oceanprotocol/ddo-js' -import { Signer, JsonRpcProvider } from 'ethers' +import { Wallet } from 'ethers' import { createHashForSignature, safeSign } from '../utils/signature.js' let envOverrides: OverrideEnvConfig[] @@ -28,7 +28,7 @@ let db: Database let oceanNode: OceanNode describe('Should validate files structure for download', () => { - let consumerAccount: Signer + let consumerAccount: Wallet before(async () => { envOverrides = buildEnvOverrideConfig( [ENVIRONMENT_VARIABLES.PRIVATE_KEY], @@ -39,8 +39,7 @@ describe('Should validate files structure for download', () => { config = await getConfiguration(true) db = await Database.init(config.dbConfig) oceanNode = OceanNode.getInstance(config, db) - const provider = new JsonRpcProvider('http://127.0.0.1:8545') - consumerAccount = (await provider.getSigner(0)) as Signer + consumerAccount = new Wallet(process.env.PRIVATE_KEY) }) const ddoObj: DDO = { diff --git a/src/test/unit/indexer/validation.test.ts b/src/test/unit/indexer/validation.test.ts index a59feb0e0..0197f8286 100644 --- a/src/test/unit/indexer/validation.test.ts +++ b/src/test/unit/indexer/validation.test.ts @@ -18,7 +18,7 @@ import { RPCS } from '../../../@types/blockchain.js' import { Database } from '../../../components/database/index.js' import { OceanNodeConfig } from '../../../@types/OceanNode.js' // import sinon, { SinonSandbox } from 'sinon' -import { Signer, JsonRpcProvider } from 'ethers' +import { Wallet } from 'ethers' import { Readable } from 'stream' import { createHashForSignature, safeSign } from '../../utils/signature.js' @@ -29,7 +29,7 @@ describe('Schema validation tests', () => { let mockDatabase: Database let config: OceanNodeConfig let oceanNode: OceanNode - let consumerAccount: Signer + let consumerAccount: Wallet // let sandbox: SinonSandbox // For token validation, please check integration test cases @@ -52,8 +52,7 @@ describe('Schema validation tests', () => { ) envOverrides = await setupEnvironment(TEST_ENV_CONFIG_FILE, envOverrides) config = await getConfiguration(true) - const provider = new JsonRpcProvider('http://127.0.0.1:8545') - consumerAccount = (await provider.getSigner(1)) as Signer + consumerAccount = new Wallet(process.env.PRIVATE_KEY) /* sandbox = sinon.createSandbox() sandbox.stub(Database, 'init').resolves({ nonce: {}, From 5f5cc852991b8454ec8947e4a7e894f33e982684 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 20:07:46 +0200 Subject: [PATCH 20/30] fix --- src/test/integration/nonce.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/integration/nonce.test.ts b/src/test/integration/nonce.test.ts index be45f9f3e..a39377ea4 100644 --- a/src/test/integration/nonce.test.ts +++ b/src/test/integration/nonce.test.ts @@ -30,6 +30,10 @@ describe('handle nonce', () => { }) it('should get nonce (1)', async () => { + await typesense.collections(nonceSchema.name).documents().create({ + id: '0x4cc9DBfc4bEeA8c986c61DAABB350C2eC55e29d1', + nonce: 1 + }) const document = await typesense .collections(nonceSchema.name) .documents() From 95695a58ab54d58897fd666e548fbf82eefeb78b Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 20:33:53 +0200 Subject: [PATCH 21/30] fix bug in ES --- src/components/database/ElasticSearchDatabase.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/database/ElasticSearchDatabase.ts b/src/components/database/ElasticSearchDatabase.ts index f43f039ff..f7984c96e 100644 --- a/src/components/database/ElasticSearchDatabase.ts +++ b/src/components/database/ElasticSearchDatabase.ts @@ -237,7 +237,9 @@ export class ElasticsearchDdoStateDatabase extends AbstractDdoStateDatabase { try { const result = await this.client.search({ index: this.index, - query + body: { + query + } }) console.log('Query result: ', result) return result.hits.hits.map((hit: any) => { From 329921856bca257098140bda6462c7ba9634962e Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 21:12:56 +0200 Subject: [PATCH 22/30] optimize test speed --- src/test/integration/download.test.ts | 31 +++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/test/integration/download.test.ts b/src/test/integration/download.test.ts index e02b041a8..0e6bfd47c 100644 --- a/src/test/integration/download.test.ts +++ b/src/test/integration/download.test.ts @@ -54,6 +54,7 @@ describe('[Download Flow] - Should run a complete node flow.', () => { let consumerAccount: Signer let orderTxId: string let publishedDataset: any + let publishedDatasetWithCredentials: any let actualDDO: any let indexer: OceanIndexer let anotherConsumer: Signer @@ -174,17 +175,28 @@ describe('[Download Flow] - Should run a complete node flow.', () => { } }) it('should publish compute datasets & algos', async function () { - this.timeout(DEFAULT_TEST_TIMEOUT * 2) + this.timeout(DEFAULT_TEST_TIMEOUT * 4) publishedDataset = await publishAsset(downloadAsset, publisherAccount) + publishedDatasetWithCredentials = await publishAsset(genericDDO, publisherAccount) const { ddo, wasTimeout } = await waitToIndex( publishedDataset.ddo.id, EVENTS.METADATA_CREATED, - DEFAULT_TEST_TIMEOUT * 2 + DEFAULT_TEST_TIMEOUT * 3 ) if (!ddo) { assert(wasTimeout === true, 'published failed due to timeout!') } + const { ddo: ddoWithCredentials, wasTimeout: wasTimeoutCredentials } = + await waitToIndex( + publishedDataset.ddo.id, + EVENTS.METADATA_CREATED, + DEFAULT_TEST_TIMEOUT * 3 + ) + + if (!ddoWithCredentials) { + assert(wasTimeoutCredentials === true, 'published failed due to timeout!') + } }) it('should fetch the published ddo', async () => { @@ -271,21 +283,8 @@ describe('[Download Flow] - Should run a complete node flow.', () => { await doCheck() }) - // for use on the test bellow - it('should publish ddo with access credentials', async function () { - publishedDataset = await publishAsset(genericDDO, publisherAccount) - const { ddo, wasTimeout } = await waitToIndex( - publishedDataset.ddo.id, - EVENTS.METADATA_CREATED, - DEFAULT_TEST_TIMEOUT - ) - - if (!ddo) { - expect(expectedTimeoutFailure(this.test.title)).to.be.equal(wasTimeout) - } - }) it('should not allow to download the asset with different consumer address', async function () { - const assetDID = publishedDataset.ddo.id + const assetDID = publishedDatasetWithCredentials.ddo.id const nonce = Date.now().toString() const messageHashBytes = createHashForSignature( await anotherConsumer.getAddress(), From 83daf56c30bc1739404546545731ca0449a0d7fa Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 21:13:08 +0200 Subject: [PATCH 23/30] force es reindex --- .../database/ElasticSearchDatabase.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/database/ElasticSearchDatabase.ts b/src/components/database/ElasticSearchDatabase.ts index f7984c96e..bdb08beab 100644 --- a/src/components/database/ElasticSearchDatabase.ts +++ b/src/components/database/ElasticSearchDatabase.ts @@ -199,7 +199,8 @@ export class ElasticsearchDdoStateDatabase extends AbstractDdoStateDatabase { index: this.index, id: did, body: { chainId, did, nft: nftAddress, txId, valid, error: errorMsg }, - refresh: 'wait_for' + // refresh: 'wait_for' + refresh: true }) return { id: did, chainId, nft: nftAddress, txId, valid, error: errorMsg } } catch (error) { @@ -239,7 +240,8 @@ export class ElasticsearchDdoStateDatabase extends AbstractDdoStateDatabase { index: this.index, body: { query - } + }, + preference: '_primary_first' }) console.log('Query result: ', result) return result.hits.hits.map((hit: any) => { @@ -280,7 +282,8 @@ export class ElasticsearchDdoStateDatabase extends AbstractDdoStateDatabase { body: { doc: { chainId, did, nft: nftAddress, txId, valid, error: errorMsg } }, - refresh: 'wait_for' + // refresh: 'wait_for' + refresh: true }) } else { return await this.create(chainId, did, nftAddress, txId, valid, errorMsg) @@ -304,7 +307,8 @@ export class ElasticsearchDdoStateDatabase extends AbstractDdoStateDatabase { await this.client.delete({ index: this.index, id: did, - refresh: 'wait_for' + // refresh: 'wait_for' + refresh: true }) return { id: did } } catch (error) { @@ -806,7 +810,8 @@ export class ElasticsearchLogDatabase extends AbstractLogDatabase { const result = await this.client.index({ index: this.index, body: { ...logEntry, timestamp }, - refresh: 'wait_for' + // refresh: 'wait_for' + refresh: true }) // uniformize result response (we need an id for the retrieveLog API) if (result._id) { @@ -956,7 +961,8 @@ export class ElasticsearchLogDatabase extends AbstractLogDatabase { await this.client.delete({ index: this.index, id: logId, - refresh: 'wait_for' + // refresh: 'wait_for' + refresh: true }) DATABASE_LOGGER.logMessageWithEmoji( `Deleted log with ID: ${logId}`, From b6cc2b3f970d99dbcab579cfa4b652731e6aa3bb Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 21:27:58 +0200 Subject: [PATCH 24/30] use custom ocean.js for system test --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 353ead21a..878431419 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -311,7 +311,7 @@ jobs: - name: Setup Ocean CLI working-directory: ${{ github.workspace }}/ocean-cli run: | - npm ci + npm install --override "@oceanprotocol/lib=github:oceanprotocol/ocean.js#feature/refactor_signatures" npm run build - name: Run system tests working-directory: ${{ github.workspace }}/ocean-cli From 2480d0ca18f4c8278fb89fe7ea32773ceb457404 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 21:51:05 +0200 Subject: [PATCH 25/30] try use ocean.js branch for system test --- .github/workflows/ci.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 878431419..dafae86bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -308,10 +308,23 @@ jobs: with: repository: 'oceanprotocol/ocean-cli' path: 'ocean-cli' + - name: Checkout Ocean-js + uses: actions/checkout@v4 + with: + repository: 'oceanprotocol/ocean.js' + path: 'ocean.js' + ref: feature/refactor_signatures + - run: npm ci + working-directory: ${{ github.workspace }}/ocean.js + - run: npm run build + working-directory: ${{ github.workspace }}/ocean.js + - run: npm link + working-directory: ${{ github.workspace }}/ocean.js - name: Setup Ocean CLI working-directory: ${{ github.workspace }}/ocean-cli run: | - npm install --override "@oceanprotocol/lib=github:oceanprotocol/ocean.js#feature/refactor_signatures" + npm ci + npm link @oceanprotocol/lib npm run build - name: Run system tests working-directory: ${{ github.workspace }}/ocean-cli From 9cda478f0f5032eaa8ed6f3084c71e9e361dd72c Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 21:59:56 +0200 Subject: [PATCH 26/30] use node v20.19.0 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dafae86bf..c2e1118ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -194,7 +194,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: 'v22.15.0' + node-version: 'v20.19.0' - name: Cache node_modules uses: actions/cache@v3 From 42840d4211213895c53fecc63497eee4b6cede02 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 22:21:56 +0200 Subject: [PATCH 27/30] one more try --- .github/workflows/ci.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2e1118ad..b495d638d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -194,7 +194,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: 'v20.19.0' + node-version: 'v22.15.0' - name: Cache node_modules uses: actions/cache@v3 @@ -308,18 +308,27 @@ jobs: with: repository: 'oceanprotocol/ocean-cli' path: 'ocean-cli' + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 'v20.19.0' - name: Checkout Ocean-js uses: actions/checkout@v4 with: repository: 'oceanprotocol/ocean.js' path: 'ocean.js' ref: feature/refactor_signatures - - run: npm ci - working-directory: ${{ github.workspace }}/ocean.js - - run: npm run build - working-directory: ${{ github.workspace }}/ocean.js - - run: npm link + - name: Build ocean-js working-directory: ${{ github.workspace }}/ocean.js + run: | + npm ci + npm run build + npm link + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 'v22.15.0' + - name: Setup Ocean CLI working-directory: ${{ github.workspace }}/ocean-cli run: | From d5a95c119f3c5590a2b573493d5f6fc10893695c Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 22:33:37 +0200 Subject: [PATCH 28/30] fix system --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b495d638d..da322e640 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -324,11 +324,6 @@ jobs: npm ci npm run build npm link - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 'v22.15.0' - - name: Setup Ocean CLI working-directory: ${{ github.workspace }}/ocean-cli run: | From b58383bf02b265110f8494054e0331f3a595741f Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 17 Feb 2026 22:44:42 +0200 Subject: [PATCH 29/30] expose nodePublicKey in root endpoint --- src/components/httpRoutes/rootEndpoint.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/httpRoutes/rootEndpoint.ts b/src/components/httpRoutes/rootEndpoint.ts index 4f3a60684..b3e2bc1f7 100644 --- a/src/components/httpRoutes/rootEndpoint.ts +++ b/src/components/httpRoutes/rootEndpoint.ts @@ -14,6 +14,7 @@ rootEndpointRoutes.get('/', async (req, res) => { nodeId: keyManager.getPeerId().toString(), chainIds: config.supportedNetworks ? Object.keys(config.supportedNetworks) : [], providerAddress: keyManager.getEthAddress(), + nodePublicKey: keyManager.getPublicKey(), serviceEndpoints: getAllServiceEndpoints(), software: 'Ocean-Node', version: '0.0.1' From b48465545fcc0629d841b20f9cde5b0c10859c08 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 18 Feb 2026 08:51:03 +0200 Subject: [PATCH 30/30] revert to integer nonces --- src/components/Auth/index.ts | 2 +- src/components/Indexer/processors/BaseProcessor.ts | 2 +- src/components/database/sqlite.ts | 2 +- src/test/unit/commands.test.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Auth/index.ts b/src/components/Auth/index.ts index 76e977afe..a043bed5c 100644 --- a/src/components/Auth/index.ts +++ b/src/components/Auth/index.ts @@ -86,7 +86,7 @@ export class Auth { const nonceCheckResult: NonceResponse = await checkNonce( oceanNode.getDatabase().nonce, address, - parseFloat(nonce), + parseInt(nonce), signature, command, chainId diff --git a/src/components/Indexer/processors/BaseProcessor.ts b/src/components/Indexer/processors/BaseProcessor.ts index f08e911da..3b98b415d 100644 --- a/src/components/Indexer/processors/BaseProcessor.ts +++ b/src/components/Indexer/processors/BaseProcessor.ts @@ -215,7 +215,7 @@ export abstract class BaseEventProcessor { { timeout: 20000 } ) return nonceResponse.status === 200 && nonceResponse.data - ? String(parseFloat(nonceResponse.data.nonce) + 1) + ? String(parseInt(nonceResponse.data.nonce) + 1) : Date.now().toString() } else { return Date.now().toString() diff --git a/src/components/database/sqlite.ts b/src/components/database/sqlite.ts index 7a51274a3..61231688e 100644 --- a/src/components/database/sqlite.ts +++ b/src/components/database/sqlite.ts @@ -24,7 +24,7 @@ export class SQLiteProvider implements DatabaseProvider { const createTableSQL = ` CREATE TABLE IF NOT EXISTS ${this.schemaNonce.name} ( id TEXT PRIMARY KEY, - nonce REAL + nonce INTEGER ); ` return new Promise((resolve, reject) => { diff --git a/src/test/unit/commands.test.ts b/src/test/unit/commands.test.ts index 0b98aa3c0..94057e56a 100644 --- a/src/test/unit/commands.test.ts +++ b/src/test/unit/commands.test.ts @@ -160,7 +160,7 @@ describe('Commands and handlers', () => { const encryptFileHandler: EncryptFileHandler = CoreHandlersRegistry.getInstance( node ).getHandler(PROTOCOL_COMMANDS.ENCRYPT_FILE) - nonce = (parseFloat(nonce) + 1).toString() + nonce = (parseInt(nonce) + 1).toString() const messageHashBytes2 = createHashForSignature( await consumerAccount.getAddress(), nonce,