From 509fd916b4c01c0636b081aa31f5bf7d65aec0c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 11:02:21 +0000 Subject: [PATCH 01/14] Bump semver from 7.3.8 to 7.5.2 Bumps [semver](https://github.com/npm/node-semver) from 7.3.8 to 7.5.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.3.8...v7.5.2) --- updated-dependencies: - dependency-name: semver dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 71 ++++++++++++++++++++++++++++++++++++++++++----- package.json | 2 +- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3fa02a43ca..25579f893f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,7 +54,7 @@ "pino-pretty": "^9.1.0", "rc": "^1.2.8", "rolling-rate-limiter": "^0.2.13", - "semver": "^7.3.7", + "semver": "^7.5.2", "sequelize": "^6.29.0", "timeout-abort-controller": "^3.0.0", "toobusy-js": "^0.5.1", @@ -2707,6 +2707,39 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@cucumber/cucumber/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@cucumber/cucumber/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@cucumber/cucumber/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@cucumber/gherkin": { "version": "26.0.3", "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.0.3.tgz", @@ -16553,9 +16586,9 @@ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -21869,6 +21902,30 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -32600,9 +32657,9 @@ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "requires": { "lru-cache": "^6.0.0" }, diff --git a/package.json b/package.json index a17d1b1ea5..fb2ac1877a 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "pino-pretty": "^9.1.0", "rc": "^1.2.8", "rolling-rate-limiter": "^0.2.13", - "semver": "^7.3.7", + "semver": "^7.5.2", "sequelize": "^6.29.0", "timeout-abort-controller": "^3.0.0", "toobusy-js": "^0.5.1", From b4c495b9718cf35345754b81ee41df592b18bbc8 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar <71610423+u-hubar@users.noreply.github.com> Date: Thu, 21 Sep 2023 12:02:27 +0200 Subject: [PATCH 02/14] Added delay for update finalization in the BDD tests (#2712) --- test/bdd/features/get.feature | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/bdd/features/get.feature b/test/bdd/features/get.feature index d11fcf2f30..88c3af8c01 100644 --- a/test/bdd/features/get.feature +++ b/test/bdd/features/get.feature @@ -59,6 +59,8 @@ Feature: Get asset states test And I wait for latest Update to finalize Then Latest Update operation finished with status: COMPLETED + And I wait for 30 seconds + When I call Update on the node 4 for the latest published UAL with validUpdate_2 And I wait for latest Update to finalize Then Latest Update operation finished with status: COMPLETED From 70b5bc17a5a78dfe5cc286497973b6119ab37674 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar <71610423+u-hubar@users.noreply.github.com> Date: Thu, 21 Sep 2023 12:24:21 +0200 Subject: [PATCH 03/14] Added decoding for EVM errors, reworked erro handling for estimateGas (#2715) * Added decoding for EVM errors, reworked logic for estimateGas error handling * Fixed function _getReturnData call * Improved EVM revert data decoding --------- Co-authored-by: djordjekovac --- .../receiver/submit-update-commit-command.js | 4 +- src/constants/constants.js | 19 +++ .../blockchain/implementation/web3-service.js | 151 ++++++++++++++++-- 3 files changed, 160 insertions(+), 14 deletions(-) diff --git a/src/commands/protocols/update/receiver/submit-update-commit-command.js b/src/commands/protocols/update/receiver/submit-update-commit-command.js index 7c40eb0f56..bf5289600a 100644 --- a/src/commands/protocols/update/receiver/submit-update-commit-command.js +++ b/src/commands/protocols/update/receiver/submit-update-commit-command.js @@ -108,8 +108,8 @@ class SubmitUpdateCommitCommand extends Command { this.logger.trace( `Scheduled submit update commit transaction for agreement id: ${agreementId} ` + `blockchain: ${blockchain} contract: ${contract}, token id: ${tokenId}, ` + - `keyword: ${keyword}, hash function id: ${hashFunctionId}, operationId ${operationId}` + - `transaction queue length: ${transactionQueueLength}.`, + `keyword: ${keyword}, hash function id: ${hashFunctionId}, operationId ${operationId}. ` + + `Transaction queue length: ${transactionQueueLength}.`, ); return Command.empty(); diff --git a/src/constants/constants.js b/src/constants/constants.js index 9458748ae7..3fff5854e4 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -8,6 +8,8 @@ export const STAKE_UINT256_MULTIPLIER_BN = UINT256_MAX_BN.div(500000000); export const UINT256_UINT32_DIVISOR_BN = UINT256_MAX_BN.div(UINT32_MAX_BN); +export const ZERO_PREFIX = '0x'; + export const ZERO_BYTES32 = `0x${'0'.repeat(64)}`; export const SCHEMA_CONTEXT = 'http://schema.org/'; @@ -21,6 +23,22 @@ export const COMMITS_DELAY_BETWEEN_NODES_IN_BLOCKS = 2; export const TRANSACTION_POLLING_TIMEOUT_MILLIS = 50 * 1000; +export const SOLIDITY_ERROR_STRING_PREFIX = '0x08c379a0'; + +export const SOLIDITY_PANIC_CODE_PREFIX = '0x4e487b71'; + +export const SOLIDITY_PANIC_REASONS = { + 0x1: 'Assertion error', + 0x11: 'Arithmetic operation underflowed or overflowed outside of an unchecked block', + 0x12: 'Division or modulo division by zero', + 0x21: 'Tried to convert a value into an enum, but the value was too big or negative', + 0x22: 'Incorrectly encoded storage byte array', + 0x31: '.pop() was called on an empty array', + 0x32: 'Array accessed at an out-of-bounds or negative index', + 0x41: 'Too much memory was allocated, or an array was created that is too large', + 0x51: 'Called a zero-initialized variable of internal function type', +}; + export const LIBP2P_KEY_DIRECTORY = 'libp2p'; export const LIBP2P_KEY_FILENAME = 'privateKey'; @@ -38,6 +56,7 @@ export const TRIPLE_STORE_CONNECT_RETRY_FREQUENCY = 10; export const MAX_FILE_SIZE = 2621440; export const GET_STATES = { LATEST: 'LATEST', FINALIZED: 'LATEST_FINALIZED' }; + export const BYTES_IN_KILOBYTE = 1024; export const BYTES_IN_MEGABYTE = BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE; diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index 5759b4d1bf..49632f426f 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -1,10 +1,14 @@ -import { ethers } from 'ethers'; +import { ethers, BigNumber } from 'ethers'; import axios from 'axios'; import async from 'async'; import { setTimeout as sleep } from 'timers/promises'; import { createRequire } from 'module'; import { + SOLIDITY_ERROR_STRING_PREFIX, + SOLIDITY_PANIC_CODE_PREFIX, + SOLIDITY_PANIC_REASONS, + ZERO_PREFIX, DEFAULT_BLOCKCHAIN_EVENT_SYNC_PERIOD_IN_MILLS, MAXIMUM_NUMBERS_OF_BLOCKS_TO_FETCH, TRANSACTION_QUEUE_CONCURRENCY, @@ -344,31 +348,47 @@ class Web3Service { let result; let gasPrice = (await this.getGasPrice()) ?? this.convertToWei(20, 'gwei'); let transactionRetried = false; + while (result === undefined) { + let gasLimit; + try { /* eslint-disable no-await-in-loop */ - const gasLimit = await contractInstance.estimateGas[functionName](...args); - const gas = gasLimit ?? this.convertToWei(900, 'kwei'); - - this.logger.info( - 'Sending signed transaction to blockchain, calling method: ' + - `${functionName} with gas limit: ${gas.toString()} and gasPrice ${gasPrice.toString()}. Transaction queue length: ${this.getTransactionQueueLength()}`, + gasLimit = await contractInstance.estimateGas[functionName](...args); + } catch (error) { + const decodedReturnData = this._decodeReturnData(error, contractInstance.interface); + await this.handleError( + Error(`gas estimation failed, reason: ${decodedReturnData}`), + functionName, ); + } + + gasLimit = gasLimit ?? this.convertToWei(900, 'kwei'); + + this.logger.info( + 'Sending signed transaction to blockchain, calling method: ' + + `${functionName} with gas limit: ${gasLimit.toString()} and gasPrice ${gasPrice.toString()}. ` + + `Transaction queue length: ${this.getTransactionQueueLength()}`, + ); + + try { const tx = await contractInstance[functionName](...args, { gasPrice, - gasLimit: gas, + gasLimit, }); result = await this.provider.waitForTransaction( tx.hash, TRANSACTION_CONFIRMATIONS, TRANSACTION_POLLING_TIMEOUT_MILLIS, ); - if (result?.status === 0) { - throw Error(); + + if (result.status === 0) { + await this.provider.call(tx, tx.blockNumber); } } catch (error) { + const decodedReturnData = this._decodeReturnData(error, contractInstance.interface); this.logger.warn( - `Failed executing smart contract function ${functionName}. Error: ${error.message}`, + `Failed executing smart contract function ${functionName}. Error: ${decodedReturnData}`, ); if ( !transactionRetried && @@ -381,13 +401,120 @@ class Web3Service { ); transactionRetried = true; } else { - await this.handleError(error, functionName); + await this.handleError( + Error(`transaction reverted, reason: ${decodedReturnData}`), + functionName, + ); } } } return result; } + _getReturnData(error) { + let nestedError = error; + while (nestedError && nestedError.error) { + nestedError = nestedError.error; + } + const errorData = nestedError.data; + + if (errorData === undefined) { + throw error; + } + + let returnData = typeof errorData === 'string' ? errorData : errorData.data; + + if (typeof returnData === 'object' && returnData.data) { + returnData = returnData.data; + } + + if (returnData === undefined || typeof returnData !== 'string') { + throw error; + } + + return returnData; + } + + _decodeReturnData(evmError, contractInterface) { + let returnData; + + try { + returnData = this._getReturnData(evmError); + } catch (error) { + return error.message; + } + + // Handle empty error data + if (returnData === ZERO_PREFIX) { + return 'Empty error data.'; + } + + // Handle standard solidity string error + if (returnData.startsWith(SOLIDITY_ERROR_STRING_PREFIX)) { + const encodedReason = returnData.slice(SOLIDITY_ERROR_STRING_PREFIX.length); + try { + return ethers.utils.defaultAbiCoder.decode(['string'], `0x${encodedReason}`)[0]; + } catch (error) { + return error.message; + } + } + + // Handle solidity panic code + if (returnData.startsWith(SOLIDITY_PANIC_CODE_PREFIX)) { + const encodedReason = returnData.slice(SOLIDITY_PANIC_CODE_PREFIX.length); + let code; + try { + [code] = ethers.utils.defaultAbiCoder.decode(['uint256'], `0x${encodedReason}`); + } catch (error) { + return error.message; + } + + return SOLIDITY_PANIC_REASONS[code] ?? 'Unknown Solidity panic code.'; + } + + // Try parsing a custom error using the contract ABI + try { + const decodedCustomError = contractInterface.parseError(returnData); + const formattedArgs = decodedCustomError.errorFragment.inputs + .map((input, i) => { + const argName = input.name; + const argValue = this._formatCustomErrorArgument(decodedCustomError.args[i]); + return `${argName}=${argValue}`; + }) + .join(', '); + return `custom error ${decodedCustomError.name}(${formattedArgs})`; + } catch (error) { + return `Failed to decode custom error data. Error: ${error}`; + } + } + + _formatCustomErrorArgument(value) { + if (value === null || value === undefined) { + return 'null'; + } + + if (typeof value === 'string') { + return value; + } + + if (typeof value === 'number' || BigNumber.isBigNumber(value)) { + return value.toString(); + } + + if (Array.isArray(value)) { + return `[${value.map((v) => this._formatCustomErrorArgument(v)).join(', ')}]`; + } + + if (typeof value === 'object') { + const formattedEntries = Object.entries(value).map( + ([k, v]) => `${k}: ${this._formatCustomErrorArgument(v)}`, + ); + return `{${formattedEntries.join(', ')}}`; + } + + return value.toString(); + } + async getAllPastEvents( blockchainId, contractName, From a97b6a4200a686ddd3dc12e02e38419777df0c74 Mon Sep 17 00:00:00 2001 From: djordjekovac Date: Thu, 21 Sep 2023 12:26:11 +0200 Subject: [PATCH 04/14] Updated command executor pause/resume logic (#2716) --- ot-node.js | 23 +++++++++++++++++++---- src/commands/command-executor.js | 28 ++++++++++++++++++---------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/ot-node.js b/ot-node.js index 5e89a6b99f..fa68cfc2af 100644 --- a/ot-node.js +++ b/ot-node.js @@ -63,13 +63,14 @@ class OTNode { await this.createProfiles(); + await this.initializeCommandExecutor(); await this.initializeShardingTableService(); await this.initializeTelemetryInjectionService(); await this.initializeBlockchainEventListenerService(); - await this.initializeCommandExecutor(); await this.initializeRouters(); await this.startNetworkModule(); + this.resumeCommandExecutor(); this.logger.info('Node is up and running!'); } @@ -244,9 +245,11 @@ class OTNode { async initializeCommandExecutor() { try { const commandExecutor = this.container.resolve('commandExecutor'); - await commandExecutor.init(); - commandExecutor.replay(); - await commandExecutor.start(); + commandExecutor.pauseQueue(); + await commandExecutor.addDefaultCommands(); + commandExecutor + .replayOldCommands() + .then(() => this.logger.info('Finished replaying old commands')); } catch (e) { this.logger.error( `Command executor initialization failed. Error message: ${e.message}`, @@ -255,6 +258,18 @@ class OTNode { } } + resumeCommandExecutor() { + try { + const commandExecutor = this.container.resolve('commandExecutor'); + commandExecutor.resumeQueue(); + } catch (e) { + this.logger.error( + `Unable to resume command executor queue. Error message: ${e.message}`, + ); + this.stop(1); + } + } + async startNetworkModule() { const networkModuleManager = this.container.resolve('networkModuleManager'); await networkModuleManager.start(); diff --git a/src/commands/command-executor.js b/src/commands/command-executor.js index e62e042acb..9f2a85933a 100644 --- a/src/commands/command-executor.js +++ b/src/commands/command-executor.js @@ -16,7 +16,6 @@ class CommandExecutor { constructor(ctx) { this.logger = ctx.logger; this.commandResolver = ctx.commandResolver; - this.started = false; this.repositoryModuleManager = ctx.repositoryModuleManager; this.verboseLoggingEnabled = ctx.config.commandExecutorVerboseLoggingEnabled; @@ -39,8 +38,8 @@ class CommandExecutor { * Initialize executor * @returns {Promise} */ - async init() { - await Promise.all(PERMANENT_COMMANDS.map((command) => this._startDefaultCommand(command))); + async addDefaultCommands() { + await Promise.all(PERMANENT_COMMANDS.map((command) => this._addDefaultCommand(command))); if (this.verboseLoggingEnabled) { this.logger.trace('Command executor has been initialized...'); @@ -48,14 +47,23 @@ class CommandExecutor { } /** - * Starts the command executor - * @return {Promise} + * Resumes the command executor queue + */ + resumeQueue() { + if (this.verboseLoggingEnabled) { + this.logger.trace('Command executor queue has been resumed...'); + } + this.queue.resume(); + } + + /** + * Pause the command executor queue */ - async start() { - this.started = true; + pauseQueue() { if (this.verboseLoggingEnabled) { - this.logger.trace('Command executor has been started...'); + this.logger.trace('Command executor queue has been paused...'); } + this.queue.pause(); } /** @@ -224,7 +232,7 @@ class CommandExecutor { * @return {Promise} * @private */ - async _startDefaultCommand(name) { + async _addDefaultCommand(name) { await this._delete(name); const handler = this.commandResolver.resolve(name); if (!handler) { @@ -398,7 +406,7 @@ class CommandExecutor { * Replays pending commands from the database * @returns {Promise} */ - async replay() { + async replayOldCommands() { this.logger.info('Replay pending/started commands from the database...'); const pendingCommands = await this.repositoryModuleManager.getCommandsWithStatus( [COMMAND_STATUS.PENDING, COMMAND_STATUS.STARTED, COMMAND_STATUS.REPEATING], From 91d9395865dbd918eaef189a8a01565e556955ed Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar <71610423+u-hubar@users.noreply.github.com> Date: Thu, 21 Sep 2023 12:54:14 +0200 Subject: [PATCH 05/14] Removed WebSocket Blockchain RPCs from the default configuration,bumped version to 6.0.16 (#2717) --- config/config.json | 8 +++----- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/config/config.json b/config/config.json index 8f3679f4c2..9906ca1803 100644 --- a/config/config.json +++ b/config/config.json @@ -131,7 +131,7 @@ "config": { "networkId": "otp::testnet", "hubContractAddress": "0x707233a55bD035C6Bc732196CA4dbffa63CbA169", - "rpcEndpoints": ["wss://lofar.origin-trail.network"], + "rpcEndpoints": ["https://lofar-tm-rpc.origin-trail.network"], "initialStakeAmount": 50000, "initialAskAmount": 2 } @@ -413,8 +413,7 @@ "hubContractAddress": "0xBbfF7Ea6b2Addc1f38A0798329e12C08f03750A6", "rpcEndpoints": [ "https://lofar-testnet.origin-trail.network", - "https://lofar-testnet.origintrail.network", - "wss://parachain-testnet-rpc.origin-trail.network" + "https://lofar-testnet.origintrail.network" ] } } @@ -562,8 +561,7 @@ "hubContractAddress": "0x5fA7916c48Fe6D5F1738d12Ad234b78c90B4cAdA", "rpcEndpoints": [ "https://astrosat-parachain-rpc.origin-trail.network", - "https://astrosat.origintrail.network/", - "wss://parachain-rpc.origin-trail.network" + "https://astrosat.origintrail.network/" ] } } diff --git a/package-lock.json b/package-lock.json index c4ee4dee95..4fdac68a57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.15", + "version": "6.0.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.15", + "version": "6.0.16", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/package.json b/package.json index 5a9a67e583..307fadd425 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.15", + "version": "6.0.16", "description": "OTNode V6", "main": "index.js", "type": "module", From 6a8492e7252918fd9de6a074f9c4ad0339a1a2dd Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar <71610423+u-hubar@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:28:55 +0200 Subject: [PATCH 06/14] OriginTrail Devnet Prerelease v6.0.16 (#2718) --- config/config.json | 8 +- ot-node.js | 23 ++- package-lock.json | 75 +++++++-- package.json | 4 +- src/commands/command-executor.js | 28 ++-- .../receiver/submit-update-commit-command.js | 4 +- src/constants/constants.js | 19 +++ .../blockchain/implementation/web3-service.js | 151 ++++++++++++++++-- test/bdd/features/get.feature | 2 + 9 files changed, 270 insertions(+), 44 deletions(-) diff --git a/config/config.json b/config/config.json index 8f3679f4c2..9906ca1803 100644 --- a/config/config.json +++ b/config/config.json @@ -131,7 +131,7 @@ "config": { "networkId": "otp::testnet", "hubContractAddress": "0x707233a55bD035C6Bc732196CA4dbffa63CbA169", - "rpcEndpoints": ["wss://lofar.origin-trail.network"], + "rpcEndpoints": ["https://lofar-tm-rpc.origin-trail.network"], "initialStakeAmount": 50000, "initialAskAmount": 2 } @@ -413,8 +413,7 @@ "hubContractAddress": "0xBbfF7Ea6b2Addc1f38A0798329e12C08f03750A6", "rpcEndpoints": [ "https://lofar-testnet.origin-trail.network", - "https://lofar-testnet.origintrail.network", - "wss://parachain-testnet-rpc.origin-trail.network" + "https://lofar-testnet.origintrail.network" ] } } @@ -562,8 +561,7 @@ "hubContractAddress": "0x5fA7916c48Fe6D5F1738d12Ad234b78c90B4cAdA", "rpcEndpoints": [ "https://astrosat-parachain-rpc.origin-trail.network", - "https://astrosat.origintrail.network/", - "wss://parachain-rpc.origin-trail.network" + "https://astrosat.origintrail.network/" ] } } diff --git a/ot-node.js b/ot-node.js index 5e89a6b99f..fa68cfc2af 100644 --- a/ot-node.js +++ b/ot-node.js @@ -63,13 +63,14 @@ class OTNode { await this.createProfiles(); + await this.initializeCommandExecutor(); await this.initializeShardingTableService(); await this.initializeTelemetryInjectionService(); await this.initializeBlockchainEventListenerService(); - await this.initializeCommandExecutor(); await this.initializeRouters(); await this.startNetworkModule(); + this.resumeCommandExecutor(); this.logger.info('Node is up and running!'); } @@ -244,9 +245,11 @@ class OTNode { async initializeCommandExecutor() { try { const commandExecutor = this.container.resolve('commandExecutor'); - await commandExecutor.init(); - commandExecutor.replay(); - await commandExecutor.start(); + commandExecutor.pauseQueue(); + await commandExecutor.addDefaultCommands(); + commandExecutor + .replayOldCommands() + .then(() => this.logger.info('Finished replaying old commands')); } catch (e) { this.logger.error( `Command executor initialization failed. Error message: ${e.message}`, @@ -255,6 +258,18 @@ class OTNode { } } + resumeCommandExecutor() { + try { + const commandExecutor = this.container.resolve('commandExecutor'); + commandExecutor.resumeQueue(); + } catch (e) { + this.logger.error( + `Unable to resume command executor queue. Error message: ${e.message}`, + ); + this.stop(1); + } + } + async startNetworkModule() { const networkModuleManager = this.container.resolve('networkModuleManager'); await networkModuleManager.start(); diff --git a/package-lock.json b/package-lock.json index 28d3231823..4fdac68a57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.15", + "version": "6.0.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.15", + "version": "6.0.16", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", @@ -54,7 +54,7 @@ "pino-pretty": "^9.1.0", "rc": "^1.2.8", "rolling-rate-limiter": "^0.2.13", - "semver": "^7.3.7", + "semver": "^7.5.2", "sequelize": "^6.29.0", "timeout-abort-controller": "^3.0.0", "toobusy-js": "^0.5.1", @@ -2707,6 +2707,39 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@cucumber/cucumber/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@cucumber/cucumber/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@cucumber/cucumber/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@cucumber/gherkin": { "version": "26.0.3", "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.0.3.tgz", @@ -16553,9 +16586,9 @@ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -21869,6 +21902,30 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -32600,9 +32657,9 @@ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "requires": { "lru-cache": "^6.0.0" }, diff --git a/package.json b/package.json index 2c8c7ec700..307fadd425 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.15", + "version": "6.0.16", "description": "OTNode V6", "main": "index.js", "type": "module", @@ -104,7 +104,7 @@ "pino-pretty": "^9.1.0", "rc": "^1.2.8", "rolling-rate-limiter": "^0.2.13", - "semver": "^7.3.7", + "semver": "^7.5.2", "sequelize": "^6.29.0", "timeout-abort-controller": "^3.0.0", "toobusy-js": "^0.5.1", diff --git a/src/commands/command-executor.js b/src/commands/command-executor.js index e62e042acb..9f2a85933a 100644 --- a/src/commands/command-executor.js +++ b/src/commands/command-executor.js @@ -16,7 +16,6 @@ class CommandExecutor { constructor(ctx) { this.logger = ctx.logger; this.commandResolver = ctx.commandResolver; - this.started = false; this.repositoryModuleManager = ctx.repositoryModuleManager; this.verboseLoggingEnabled = ctx.config.commandExecutorVerboseLoggingEnabled; @@ -39,8 +38,8 @@ class CommandExecutor { * Initialize executor * @returns {Promise} */ - async init() { - await Promise.all(PERMANENT_COMMANDS.map((command) => this._startDefaultCommand(command))); + async addDefaultCommands() { + await Promise.all(PERMANENT_COMMANDS.map((command) => this._addDefaultCommand(command))); if (this.verboseLoggingEnabled) { this.logger.trace('Command executor has been initialized...'); @@ -48,14 +47,23 @@ class CommandExecutor { } /** - * Starts the command executor - * @return {Promise} + * Resumes the command executor queue + */ + resumeQueue() { + if (this.verboseLoggingEnabled) { + this.logger.trace('Command executor queue has been resumed...'); + } + this.queue.resume(); + } + + /** + * Pause the command executor queue */ - async start() { - this.started = true; + pauseQueue() { if (this.verboseLoggingEnabled) { - this.logger.trace('Command executor has been started...'); + this.logger.trace('Command executor queue has been paused...'); } + this.queue.pause(); } /** @@ -224,7 +232,7 @@ class CommandExecutor { * @return {Promise} * @private */ - async _startDefaultCommand(name) { + async _addDefaultCommand(name) { await this._delete(name); const handler = this.commandResolver.resolve(name); if (!handler) { @@ -398,7 +406,7 @@ class CommandExecutor { * Replays pending commands from the database * @returns {Promise} */ - async replay() { + async replayOldCommands() { this.logger.info('Replay pending/started commands from the database...'); const pendingCommands = await this.repositoryModuleManager.getCommandsWithStatus( [COMMAND_STATUS.PENDING, COMMAND_STATUS.STARTED, COMMAND_STATUS.REPEATING], diff --git a/src/commands/protocols/update/receiver/submit-update-commit-command.js b/src/commands/protocols/update/receiver/submit-update-commit-command.js index 7c40eb0f56..bf5289600a 100644 --- a/src/commands/protocols/update/receiver/submit-update-commit-command.js +++ b/src/commands/protocols/update/receiver/submit-update-commit-command.js @@ -108,8 +108,8 @@ class SubmitUpdateCommitCommand extends Command { this.logger.trace( `Scheduled submit update commit transaction for agreement id: ${agreementId} ` + `blockchain: ${blockchain} contract: ${contract}, token id: ${tokenId}, ` + - `keyword: ${keyword}, hash function id: ${hashFunctionId}, operationId ${operationId}` + - `transaction queue length: ${transactionQueueLength}.`, + `keyword: ${keyword}, hash function id: ${hashFunctionId}, operationId ${operationId}. ` + + `Transaction queue length: ${transactionQueueLength}.`, ); return Command.empty(); diff --git a/src/constants/constants.js b/src/constants/constants.js index 9458748ae7..3fff5854e4 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -8,6 +8,8 @@ export const STAKE_UINT256_MULTIPLIER_BN = UINT256_MAX_BN.div(500000000); export const UINT256_UINT32_DIVISOR_BN = UINT256_MAX_BN.div(UINT32_MAX_BN); +export const ZERO_PREFIX = '0x'; + export const ZERO_BYTES32 = `0x${'0'.repeat(64)}`; export const SCHEMA_CONTEXT = 'http://schema.org/'; @@ -21,6 +23,22 @@ export const COMMITS_DELAY_BETWEEN_NODES_IN_BLOCKS = 2; export const TRANSACTION_POLLING_TIMEOUT_MILLIS = 50 * 1000; +export const SOLIDITY_ERROR_STRING_PREFIX = '0x08c379a0'; + +export const SOLIDITY_PANIC_CODE_PREFIX = '0x4e487b71'; + +export const SOLIDITY_PANIC_REASONS = { + 0x1: 'Assertion error', + 0x11: 'Arithmetic operation underflowed or overflowed outside of an unchecked block', + 0x12: 'Division or modulo division by zero', + 0x21: 'Tried to convert a value into an enum, but the value was too big or negative', + 0x22: 'Incorrectly encoded storage byte array', + 0x31: '.pop() was called on an empty array', + 0x32: 'Array accessed at an out-of-bounds or negative index', + 0x41: 'Too much memory was allocated, or an array was created that is too large', + 0x51: 'Called a zero-initialized variable of internal function type', +}; + export const LIBP2P_KEY_DIRECTORY = 'libp2p'; export const LIBP2P_KEY_FILENAME = 'privateKey'; @@ -38,6 +56,7 @@ export const TRIPLE_STORE_CONNECT_RETRY_FREQUENCY = 10; export const MAX_FILE_SIZE = 2621440; export const GET_STATES = { LATEST: 'LATEST', FINALIZED: 'LATEST_FINALIZED' }; + export const BYTES_IN_KILOBYTE = 1024; export const BYTES_IN_MEGABYTE = BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE; diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index 5759b4d1bf..49632f426f 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -1,10 +1,14 @@ -import { ethers } from 'ethers'; +import { ethers, BigNumber } from 'ethers'; import axios from 'axios'; import async from 'async'; import { setTimeout as sleep } from 'timers/promises'; import { createRequire } from 'module'; import { + SOLIDITY_ERROR_STRING_PREFIX, + SOLIDITY_PANIC_CODE_PREFIX, + SOLIDITY_PANIC_REASONS, + ZERO_PREFIX, DEFAULT_BLOCKCHAIN_EVENT_SYNC_PERIOD_IN_MILLS, MAXIMUM_NUMBERS_OF_BLOCKS_TO_FETCH, TRANSACTION_QUEUE_CONCURRENCY, @@ -344,31 +348,47 @@ class Web3Service { let result; let gasPrice = (await this.getGasPrice()) ?? this.convertToWei(20, 'gwei'); let transactionRetried = false; + while (result === undefined) { + let gasLimit; + try { /* eslint-disable no-await-in-loop */ - const gasLimit = await contractInstance.estimateGas[functionName](...args); - const gas = gasLimit ?? this.convertToWei(900, 'kwei'); - - this.logger.info( - 'Sending signed transaction to blockchain, calling method: ' + - `${functionName} with gas limit: ${gas.toString()} and gasPrice ${gasPrice.toString()}. Transaction queue length: ${this.getTransactionQueueLength()}`, + gasLimit = await contractInstance.estimateGas[functionName](...args); + } catch (error) { + const decodedReturnData = this._decodeReturnData(error, contractInstance.interface); + await this.handleError( + Error(`gas estimation failed, reason: ${decodedReturnData}`), + functionName, ); + } + + gasLimit = gasLimit ?? this.convertToWei(900, 'kwei'); + + this.logger.info( + 'Sending signed transaction to blockchain, calling method: ' + + `${functionName} with gas limit: ${gasLimit.toString()} and gasPrice ${gasPrice.toString()}. ` + + `Transaction queue length: ${this.getTransactionQueueLength()}`, + ); + + try { const tx = await contractInstance[functionName](...args, { gasPrice, - gasLimit: gas, + gasLimit, }); result = await this.provider.waitForTransaction( tx.hash, TRANSACTION_CONFIRMATIONS, TRANSACTION_POLLING_TIMEOUT_MILLIS, ); - if (result?.status === 0) { - throw Error(); + + if (result.status === 0) { + await this.provider.call(tx, tx.blockNumber); } } catch (error) { + const decodedReturnData = this._decodeReturnData(error, contractInstance.interface); this.logger.warn( - `Failed executing smart contract function ${functionName}. Error: ${error.message}`, + `Failed executing smart contract function ${functionName}. Error: ${decodedReturnData}`, ); if ( !transactionRetried && @@ -381,13 +401,120 @@ class Web3Service { ); transactionRetried = true; } else { - await this.handleError(error, functionName); + await this.handleError( + Error(`transaction reverted, reason: ${decodedReturnData}`), + functionName, + ); } } } return result; } + _getReturnData(error) { + let nestedError = error; + while (nestedError && nestedError.error) { + nestedError = nestedError.error; + } + const errorData = nestedError.data; + + if (errorData === undefined) { + throw error; + } + + let returnData = typeof errorData === 'string' ? errorData : errorData.data; + + if (typeof returnData === 'object' && returnData.data) { + returnData = returnData.data; + } + + if (returnData === undefined || typeof returnData !== 'string') { + throw error; + } + + return returnData; + } + + _decodeReturnData(evmError, contractInterface) { + let returnData; + + try { + returnData = this._getReturnData(evmError); + } catch (error) { + return error.message; + } + + // Handle empty error data + if (returnData === ZERO_PREFIX) { + return 'Empty error data.'; + } + + // Handle standard solidity string error + if (returnData.startsWith(SOLIDITY_ERROR_STRING_PREFIX)) { + const encodedReason = returnData.slice(SOLIDITY_ERROR_STRING_PREFIX.length); + try { + return ethers.utils.defaultAbiCoder.decode(['string'], `0x${encodedReason}`)[0]; + } catch (error) { + return error.message; + } + } + + // Handle solidity panic code + if (returnData.startsWith(SOLIDITY_PANIC_CODE_PREFIX)) { + const encodedReason = returnData.slice(SOLIDITY_PANIC_CODE_PREFIX.length); + let code; + try { + [code] = ethers.utils.defaultAbiCoder.decode(['uint256'], `0x${encodedReason}`); + } catch (error) { + return error.message; + } + + return SOLIDITY_PANIC_REASONS[code] ?? 'Unknown Solidity panic code.'; + } + + // Try parsing a custom error using the contract ABI + try { + const decodedCustomError = contractInterface.parseError(returnData); + const formattedArgs = decodedCustomError.errorFragment.inputs + .map((input, i) => { + const argName = input.name; + const argValue = this._formatCustomErrorArgument(decodedCustomError.args[i]); + return `${argName}=${argValue}`; + }) + .join(', '); + return `custom error ${decodedCustomError.name}(${formattedArgs})`; + } catch (error) { + return `Failed to decode custom error data. Error: ${error}`; + } + } + + _formatCustomErrorArgument(value) { + if (value === null || value === undefined) { + return 'null'; + } + + if (typeof value === 'string') { + return value; + } + + if (typeof value === 'number' || BigNumber.isBigNumber(value)) { + return value.toString(); + } + + if (Array.isArray(value)) { + return `[${value.map((v) => this._formatCustomErrorArgument(v)).join(', ')}]`; + } + + if (typeof value === 'object') { + const formattedEntries = Object.entries(value).map( + ([k, v]) => `${k}: ${this._formatCustomErrorArgument(v)}`, + ); + return `{${formattedEntries.join(', ')}}`; + } + + return value.toString(); + } + async getAllPastEvents( blockchainId, contractName, diff --git a/test/bdd/features/get.feature b/test/bdd/features/get.feature index d11fcf2f30..88c3af8c01 100644 --- a/test/bdd/features/get.feature +++ b/test/bdd/features/get.feature @@ -59,6 +59,8 @@ Feature: Get asset states test And I wait for latest Update to finalize Then Latest Update operation finished with status: COMPLETED + And I wait for 30 seconds + When I call Update on the node 4 for the latest published UAL with validUpdate_2 And I wait for latest Update to finalize Then Latest Update operation finished with status: COMPLETED From 5374226ca99a79e297c542a37fba0d037f796a56 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar <71610423+u-hubar@users.noreply.github.com> Date: Fri, 22 Sep 2023 09:12:14 +0200 Subject: [PATCH 07/14] Revert "OriginTrail Devnet Prerelease v6.0.16 (#2718)" This reverts commit 6a8492e7252918fd9de6a074f9c4ad0339a1a2dd. --- config/config.json | 8 +- ot-node.js | 23 +-- package-lock.json | 75 ++------- package.json | 4 +- src/commands/command-executor.js | 28 ++-- .../receiver/submit-update-commit-command.js | 4 +- src/constants/constants.js | 19 --- .../blockchain/implementation/web3-service.js | 151 ++---------------- test/bdd/features/get.feature | 2 - 9 files changed, 44 insertions(+), 270 deletions(-) diff --git a/config/config.json b/config/config.json index 9906ca1803..8f3679f4c2 100644 --- a/config/config.json +++ b/config/config.json @@ -131,7 +131,7 @@ "config": { "networkId": "otp::testnet", "hubContractAddress": "0x707233a55bD035C6Bc732196CA4dbffa63CbA169", - "rpcEndpoints": ["https://lofar-tm-rpc.origin-trail.network"], + "rpcEndpoints": ["wss://lofar.origin-trail.network"], "initialStakeAmount": 50000, "initialAskAmount": 2 } @@ -413,7 +413,8 @@ "hubContractAddress": "0xBbfF7Ea6b2Addc1f38A0798329e12C08f03750A6", "rpcEndpoints": [ "https://lofar-testnet.origin-trail.network", - "https://lofar-testnet.origintrail.network" + "https://lofar-testnet.origintrail.network", + "wss://parachain-testnet-rpc.origin-trail.network" ] } } @@ -561,7 +562,8 @@ "hubContractAddress": "0x5fA7916c48Fe6D5F1738d12Ad234b78c90B4cAdA", "rpcEndpoints": [ "https://astrosat-parachain-rpc.origin-trail.network", - "https://astrosat.origintrail.network/" + "https://astrosat.origintrail.network/", + "wss://parachain-rpc.origin-trail.network" ] } } diff --git a/ot-node.js b/ot-node.js index fa68cfc2af..5e89a6b99f 100644 --- a/ot-node.js +++ b/ot-node.js @@ -63,14 +63,13 @@ class OTNode { await this.createProfiles(); - await this.initializeCommandExecutor(); await this.initializeShardingTableService(); await this.initializeTelemetryInjectionService(); await this.initializeBlockchainEventListenerService(); + await this.initializeCommandExecutor(); await this.initializeRouters(); await this.startNetworkModule(); - this.resumeCommandExecutor(); this.logger.info('Node is up and running!'); } @@ -245,11 +244,9 @@ class OTNode { async initializeCommandExecutor() { try { const commandExecutor = this.container.resolve('commandExecutor'); - commandExecutor.pauseQueue(); - await commandExecutor.addDefaultCommands(); - commandExecutor - .replayOldCommands() - .then(() => this.logger.info('Finished replaying old commands')); + await commandExecutor.init(); + commandExecutor.replay(); + await commandExecutor.start(); } catch (e) { this.logger.error( `Command executor initialization failed. Error message: ${e.message}`, @@ -258,18 +255,6 @@ class OTNode { } } - resumeCommandExecutor() { - try { - const commandExecutor = this.container.resolve('commandExecutor'); - commandExecutor.resumeQueue(); - } catch (e) { - this.logger.error( - `Unable to resume command executor queue. Error message: ${e.message}`, - ); - this.stop(1); - } - } - async startNetworkModule() { const networkModuleManager = this.container.resolve('networkModuleManager'); await networkModuleManager.start(); diff --git a/package-lock.json b/package-lock.json index 4fdac68a57..28d3231823 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.15", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.15", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", @@ -54,7 +54,7 @@ "pino-pretty": "^9.1.0", "rc": "^1.2.8", "rolling-rate-limiter": "^0.2.13", - "semver": "^7.5.2", + "semver": "^7.3.7", "sequelize": "^6.29.0", "timeout-abort-controller": "^3.0.0", "toobusy-js": "^0.5.1", @@ -2707,39 +2707,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@cucumber/cucumber/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@cucumber/cucumber/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@cucumber/cucumber/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@cucumber/gherkin": { "version": "26.0.3", "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.0.3.tgz", @@ -16586,9 +16553,9 @@ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "node_modules/semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -21902,30 +21869,6 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } }, @@ -32657,9 +32600,9 @@ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "requires": { "lru-cache": "^6.0.0" }, diff --git a/package.json b/package.json index 307fadd425..2c8c7ec700 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.15", "description": "OTNode V6", "main": "index.js", "type": "module", @@ -104,7 +104,7 @@ "pino-pretty": "^9.1.0", "rc": "^1.2.8", "rolling-rate-limiter": "^0.2.13", - "semver": "^7.5.2", + "semver": "^7.3.7", "sequelize": "^6.29.0", "timeout-abort-controller": "^3.0.0", "toobusy-js": "^0.5.1", diff --git a/src/commands/command-executor.js b/src/commands/command-executor.js index 9f2a85933a..e62e042acb 100644 --- a/src/commands/command-executor.js +++ b/src/commands/command-executor.js @@ -16,6 +16,7 @@ class CommandExecutor { constructor(ctx) { this.logger = ctx.logger; this.commandResolver = ctx.commandResolver; + this.started = false; this.repositoryModuleManager = ctx.repositoryModuleManager; this.verboseLoggingEnabled = ctx.config.commandExecutorVerboseLoggingEnabled; @@ -38,8 +39,8 @@ class CommandExecutor { * Initialize executor * @returns {Promise} */ - async addDefaultCommands() { - await Promise.all(PERMANENT_COMMANDS.map((command) => this._addDefaultCommand(command))); + async init() { + await Promise.all(PERMANENT_COMMANDS.map((command) => this._startDefaultCommand(command))); if (this.verboseLoggingEnabled) { this.logger.trace('Command executor has been initialized...'); @@ -47,23 +48,14 @@ class CommandExecutor { } /** - * Resumes the command executor queue - */ - resumeQueue() { - if (this.verboseLoggingEnabled) { - this.logger.trace('Command executor queue has been resumed...'); - } - this.queue.resume(); - } - - /** - * Pause the command executor queue + * Starts the command executor + * @return {Promise} */ - pauseQueue() { + async start() { + this.started = true; if (this.verboseLoggingEnabled) { - this.logger.trace('Command executor queue has been paused...'); + this.logger.trace('Command executor has been started...'); } - this.queue.pause(); } /** @@ -232,7 +224,7 @@ class CommandExecutor { * @return {Promise} * @private */ - async _addDefaultCommand(name) { + async _startDefaultCommand(name) { await this._delete(name); const handler = this.commandResolver.resolve(name); if (!handler) { @@ -406,7 +398,7 @@ class CommandExecutor { * Replays pending commands from the database * @returns {Promise} */ - async replayOldCommands() { + async replay() { this.logger.info('Replay pending/started commands from the database...'); const pendingCommands = await this.repositoryModuleManager.getCommandsWithStatus( [COMMAND_STATUS.PENDING, COMMAND_STATUS.STARTED, COMMAND_STATUS.REPEATING], diff --git a/src/commands/protocols/update/receiver/submit-update-commit-command.js b/src/commands/protocols/update/receiver/submit-update-commit-command.js index bf5289600a..7c40eb0f56 100644 --- a/src/commands/protocols/update/receiver/submit-update-commit-command.js +++ b/src/commands/protocols/update/receiver/submit-update-commit-command.js @@ -108,8 +108,8 @@ class SubmitUpdateCommitCommand extends Command { this.logger.trace( `Scheduled submit update commit transaction for agreement id: ${agreementId} ` + `blockchain: ${blockchain} contract: ${contract}, token id: ${tokenId}, ` + - `keyword: ${keyword}, hash function id: ${hashFunctionId}, operationId ${operationId}. ` + - `Transaction queue length: ${transactionQueueLength}.`, + `keyword: ${keyword}, hash function id: ${hashFunctionId}, operationId ${operationId}` + + `transaction queue length: ${transactionQueueLength}.`, ); return Command.empty(); diff --git a/src/constants/constants.js b/src/constants/constants.js index 3fff5854e4..9458748ae7 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -8,8 +8,6 @@ export const STAKE_UINT256_MULTIPLIER_BN = UINT256_MAX_BN.div(500000000); export const UINT256_UINT32_DIVISOR_BN = UINT256_MAX_BN.div(UINT32_MAX_BN); -export const ZERO_PREFIX = '0x'; - export const ZERO_BYTES32 = `0x${'0'.repeat(64)}`; export const SCHEMA_CONTEXT = 'http://schema.org/'; @@ -23,22 +21,6 @@ export const COMMITS_DELAY_BETWEEN_NODES_IN_BLOCKS = 2; export const TRANSACTION_POLLING_TIMEOUT_MILLIS = 50 * 1000; -export const SOLIDITY_ERROR_STRING_PREFIX = '0x08c379a0'; - -export const SOLIDITY_PANIC_CODE_PREFIX = '0x4e487b71'; - -export const SOLIDITY_PANIC_REASONS = { - 0x1: 'Assertion error', - 0x11: 'Arithmetic operation underflowed or overflowed outside of an unchecked block', - 0x12: 'Division or modulo division by zero', - 0x21: 'Tried to convert a value into an enum, but the value was too big or negative', - 0x22: 'Incorrectly encoded storage byte array', - 0x31: '.pop() was called on an empty array', - 0x32: 'Array accessed at an out-of-bounds or negative index', - 0x41: 'Too much memory was allocated, or an array was created that is too large', - 0x51: 'Called a zero-initialized variable of internal function type', -}; - export const LIBP2P_KEY_DIRECTORY = 'libp2p'; export const LIBP2P_KEY_FILENAME = 'privateKey'; @@ -56,7 +38,6 @@ export const TRIPLE_STORE_CONNECT_RETRY_FREQUENCY = 10; export const MAX_FILE_SIZE = 2621440; export const GET_STATES = { LATEST: 'LATEST', FINALIZED: 'LATEST_FINALIZED' }; - export const BYTES_IN_KILOBYTE = 1024; export const BYTES_IN_MEGABYTE = BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE; diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index 49632f426f..5759b4d1bf 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -1,14 +1,10 @@ -import { ethers, BigNumber } from 'ethers'; +import { ethers } from 'ethers'; import axios from 'axios'; import async from 'async'; import { setTimeout as sleep } from 'timers/promises'; import { createRequire } from 'module'; import { - SOLIDITY_ERROR_STRING_PREFIX, - SOLIDITY_PANIC_CODE_PREFIX, - SOLIDITY_PANIC_REASONS, - ZERO_PREFIX, DEFAULT_BLOCKCHAIN_EVENT_SYNC_PERIOD_IN_MILLS, MAXIMUM_NUMBERS_OF_BLOCKS_TO_FETCH, TRANSACTION_QUEUE_CONCURRENCY, @@ -348,47 +344,31 @@ class Web3Service { let result; let gasPrice = (await this.getGasPrice()) ?? this.convertToWei(20, 'gwei'); let transactionRetried = false; - while (result === undefined) { - let gasLimit; - try { /* eslint-disable no-await-in-loop */ - gasLimit = await contractInstance.estimateGas[functionName](...args); - } catch (error) { - const decodedReturnData = this._decodeReturnData(error, contractInstance.interface); - await this.handleError( - Error(`gas estimation failed, reason: ${decodedReturnData}`), - functionName, - ); - } - - gasLimit = gasLimit ?? this.convertToWei(900, 'kwei'); - - this.logger.info( - 'Sending signed transaction to blockchain, calling method: ' + - `${functionName} with gas limit: ${gasLimit.toString()} and gasPrice ${gasPrice.toString()}. ` + - `Transaction queue length: ${this.getTransactionQueueLength()}`, - ); + const gasLimit = await contractInstance.estimateGas[functionName](...args); + const gas = gasLimit ?? this.convertToWei(900, 'kwei'); - try { + this.logger.info( + 'Sending signed transaction to blockchain, calling method: ' + + `${functionName} with gas limit: ${gas.toString()} and gasPrice ${gasPrice.toString()}. Transaction queue length: ${this.getTransactionQueueLength()}`, + ); const tx = await contractInstance[functionName](...args, { gasPrice, - gasLimit, + gasLimit: gas, }); result = await this.provider.waitForTransaction( tx.hash, TRANSACTION_CONFIRMATIONS, TRANSACTION_POLLING_TIMEOUT_MILLIS, ); - - if (result.status === 0) { - await this.provider.call(tx, tx.blockNumber); + if (result?.status === 0) { + throw Error(); } } catch (error) { - const decodedReturnData = this._decodeReturnData(error, contractInstance.interface); this.logger.warn( - `Failed executing smart contract function ${functionName}. Error: ${decodedReturnData}`, + `Failed executing smart contract function ${functionName}. Error: ${error.message}`, ); if ( !transactionRetried && @@ -401,120 +381,13 @@ class Web3Service { ); transactionRetried = true; } else { - await this.handleError( - Error(`transaction reverted, reason: ${decodedReturnData}`), - functionName, - ); + await this.handleError(error, functionName); } } } return result; } - _getReturnData(error) { - let nestedError = error; - while (nestedError && nestedError.error) { - nestedError = nestedError.error; - } - const errorData = nestedError.data; - - if (errorData === undefined) { - throw error; - } - - let returnData = typeof errorData === 'string' ? errorData : errorData.data; - - if (typeof returnData === 'object' && returnData.data) { - returnData = returnData.data; - } - - if (returnData === undefined || typeof returnData !== 'string') { - throw error; - } - - return returnData; - } - - _decodeReturnData(evmError, contractInterface) { - let returnData; - - try { - returnData = this._getReturnData(evmError); - } catch (error) { - return error.message; - } - - // Handle empty error data - if (returnData === ZERO_PREFIX) { - return 'Empty error data.'; - } - - // Handle standard solidity string error - if (returnData.startsWith(SOLIDITY_ERROR_STRING_PREFIX)) { - const encodedReason = returnData.slice(SOLIDITY_ERROR_STRING_PREFIX.length); - try { - return ethers.utils.defaultAbiCoder.decode(['string'], `0x${encodedReason}`)[0]; - } catch (error) { - return error.message; - } - } - - // Handle solidity panic code - if (returnData.startsWith(SOLIDITY_PANIC_CODE_PREFIX)) { - const encodedReason = returnData.slice(SOLIDITY_PANIC_CODE_PREFIX.length); - let code; - try { - [code] = ethers.utils.defaultAbiCoder.decode(['uint256'], `0x${encodedReason}`); - } catch (error) { - return error.message; - } - - return SOLIDITY_PANIC_REASONS[code] ?? 'Unknown Solidity panic code.'; - } - - // Try parsing a custom error using the contract ABI - try { - const decodedCustomError = contractInterface.parseError(returnData); - const formattedArgs = decodedCustomError.errorFragment.inputs - .map((input, i) => { - const argName = input.name; - const argValue = this._formatCustomErrorArgument(decodedCustomError.args[i]); - return `${argName}=${argValue}`; - }) - .join(', '); - return `custom error ${decodedCustomError.name}(${formattedArgs})`; - } catch (error) { - return `Failed to decode custom error data. Error: ${error}`; - } - } - - _formatCustomErrorArgument(value) { - if (value === null || value === undefined) { - return 'null'; - } - - if (typeof value === 'string') { - return value; - } - - if (typeof value === 'number' || BigNumber.isBigNumber(value)) { - return value.toString(); - } - - if (Array.isArray(value)) { - return `[${value.map((v) => this._formatCustomErrorArgument(v)).join(', ')}]`; - } - - if (typeof value === 'object') { - const formattedEntries = Object.entries(value).map( - ([k, v]) => `${k}: ${this._formatCustomErrorArgument(v)}`, - ); - return `{${formattedEntries.join(', ')}}`; - } - - return value.toString(); - } - async getAllPastEvents( blockchainId, contractName, diff --git a/test/bdd/features/get.feature b/test/bdd/features/get.feature index 88c3af8c01..d11fcf2f30 100644 --- a/test/bdd/features/get.feature +++ b/test/bdd/features/get.feature @@ -59,8 +59,6 @@ Feature: Get asset states test And I wait for latest Update to finalize Then Latest Update operation finished with status: COMPLETED - And I wait for 30 seconds - When I call Update on the node 4 for the latest published UAL with validUpdate_2 And I wait for latest Update to finalize Then Latest Update operation finished with status: COMPLETED From 5e937469f9ab9bdd35dc7a04e7c6b89fe92a5423 Mon Sep 17 00:00:00 2001 From: Djordje Kovacevic Date: Fri, 22 Sep 2023 09:38:15 +0200 Subject: [PATCH 08/14] Authorization middleware logic fixed --- .../implementation/middleware/authorization-middleware.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/http-client/implementation/middleware/authorization-middleware.js b/src/modules/http-client/implementation/middleware/authorization-middleware.js index 3c9c03ca58..26e54b191e 100644 --- a/src/modules/http-client/implementation/middleware/authorization-middleware.js +++ b/src/modules/http-client/implementation/middleware/authorization-middleware.js @@ -5,7 +5,8 @@ const getToken = (req) => { }; export default (authService) => async (req, res, next) => { - const operation = req.url.split('/')[1].split('?')[0].toUpperCase(); + const urlElements = req.url.split('/'); + const operation = urlElements[urlElements.length() - 1].split('?')[0].toUpperCase(); if (authService.isPublicOperation(operation)) { return next(); From 51c722727de5aaec37447e582d634b53c11a763b Mon Sep 17 00:00:00 2001 From: Djordje Kovacevic Date: Fri, 22 Sep 2023 09:41:45 +0200 Subject: [PATCH 09/14] Typo fixed --- .../implementation/middleware/authorization-middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/http-client/implementation/middleware/authorization-middleware.js b/src/modules/http-client/implementation/middleware/authorization-middleware.js index 26e54b191e..b0cd19ac2c 100644 --- a/src/modules/http-client/implementation/middleware/authorization-middleware.js +++ b/src/modules/http-client/implementation/middleware/authorization-middleware.js @@ -6,7 +6,7 @@ const getToken = (req) => { export default (authService) => async (req, res, next) => { const urlElements = req.url.split('/'); - const operation = urlElements[urlElements.length() - 1].split('?')[0].toUpperCase(); + const operation = urlElements[urlElements.length - 1].split('?')[0].toUpperCase(); if (authService.isPublicOperation(operation)) { return next(); From e028a5ad7ccc8ca82f1dbb24622855f0ceb1a4df Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 22 Sep 2023 10:21:11 +0200 Subject: [PATCH 10/14] Update auth middleware to support versioned endpoints --- .../implementation/middleware/authorization-middleware.js | 7 +++++-- src/service/auth-service.js | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/modules/http-client/implementation/middleware/authorization-middleware.js b/src/modules/http-client/implementation/middleware/authorization-middleware.js index b0cd19ac2c..5c92020f15 100644 --- a/src/modules/http-client/implementation/middleware/authorization-middleware.js +++ b/src/modules/http-client/implementation/middleware/authorization-middleware.js @@ -5,8 +5,11 @@ const getToken = (req) => { }; export default (authService) => async (req, res, next) => { - const urlElements = req.url.split('/'); - const operation = urlElements[urlElements.length - 1].split('?')[0].toUpperCase(); + // eslint-disable-next-line no-useless-escape + const match = req.path.match(/^\/(?:v[0-9]+\/)?([^\/\?]+)/); + if (!match) return res.status(404).send('Not found.'); + + const operation = match[0].substring(1); if (authService.isPublicOperation(operation)) { return next(); diff --git a/src/service/auth-service.js b/src/service/auth-service.js index 729923934c..49a0a65ee1 100644 --- a/src/service/auth-service.js +++ b/src/service/auth-service.js @@ -62,7 +62,10 @@ class AuthService { return false; } - return this._authConfig.publicOperations.includes(operationName); + return ( + this._authConfig.publicOperations.includes(`v0/${operationName}`) || + this._authConfig.publicOperations.includes(operationName) + ); } /** From 909ef8505b3bf24060b172270f7ab44c1e3accbc Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 22 Sep 2023 10:32:05 +0200 Subject: [PATCH 11/14] Updated authentication middleware to work in the same way as authorization, fixed unit tests --- .../implementation/middleware/authentication-middleware.js | 6 +++++- test/unit/middleware/authentication-middleware.test.js | 6 +++--- test/unit/middleware/authorization-middleware.test.js | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/modules/http-client/implementation/middleware/authentication-middleware.js b/src/modules/http-client/implementation/middleware/authentication-middleware.js index 87bdf99454..bb3ce98246 100644 --- a/src/modules/http-client/implementation/middleware/authentication-middleware.js +++ b/src/modules/http-client/implementation/middleware/authentication-middleware.js @@ -14,7 +14,11 @@ const parseIp = (req) => { }; export default (authService) => async (req, res, next) => { - const operation = req.url.split('/')[1].split('?')[0].toUpperCase(); + // eslint-disable-next-line no-useless-escape + const match = req.path.match(/^\/(?:v[0-9]+\/)?([^\/\?]+)/); + if (!match) return res.status(404).send('Not found.'); + + const operation = match[0].substring(1); if (authService.isPublicOperation(operation)) { return next(); diff --git a/test/unit/middleware/authentication-middleware.test.js b/test/unit/middleware/authentication-middleware.test.js index cd67d9b888..287af08f03 100644 --- a/test/unit/middleware/authentication-middleware.test.js +++ b/test/unit/middleware/authentication-middleware.test.js @@ -25,7 +25,7 @@ describe('authentication middleware test', async () => { }), ); - const req = { headers: { authorization: 'Bearer token' }, url: '/publish' }; + const req = { headers: { authorization: 'Bearer token' }, path: '/publish' }; const spySend = sandbox.spy(); const spyStatus = sandbox.spy(() => ({ send: spySend })); @@ -46,7 +46,7 @@ describe('authentication middleware test', async () => { }), ); - const req = { headers: { authorization: 'Bearer token' }, url: '/publish' }; + const req = { headers: { authorization: 'Bearer token' }, path: '/publish' }; const spySend = sandbox.spy(); const spyStatus = sandbox.spy(() => ({ send: spySend })); @@ -67,7 +67,7 @@ describe('authentication middleware test', async () => { }), ); - const req = { headers: { authorization: 'Bearer token' }, url: '/publish' }; + const req = { headers: { authorization: 'Bearer token' }, path: '/publish' }; const spySend = sandbox.spy(); const spyStatus = sandbox.spy(() => ({ send: spySend })); diff --git a/test/unit/middleware/authorization-middleware.test.js b/test/unit/middleware/authorization-middleware.test.js index 6d254394f8..6e0dd30714 100644 --- a/test/unit/middleware/authorization-middleware.test.js +++ b/test/unit/middleware/authorization-middleware.test.js @@ -26,7 +26,7 @@ describe('authentication middleware test', async () => { }), ); - const req = { headers: { authorization: 'Bearer token' }, url: '/publish' }; + const req = { headers: { authorization: 'Bearer token' }, path: '/publish' }; const spySend = sandbox.spy(); const spyStatus = sandbox.spy(() => ({ send: spySend })); @@ -46,7 +46,7 @@ describe('authentication middleware test', async () => { }), ); - const req = { headers: { authorization: 'Bearer token' }, url: '/publish' }; + const req = { headers: { authorization: 'Bearer token' }, path: '/publish' }; const spySend = sandbox.spy(); const spyStatus = sandbox.spy(() => ({ send: spySend })); @@ -67,7 +67,7 @@ describe('authentication middleware test', async () => { }), ); - const req = { headers: { authorization: 'Bearer token' }, url: '/publish' }; + const req = { headers: { authorization: 'Bearer token' }, path: '/publish' }; const spySend = sandbox.spy(); const spyStatus = sandbox.spy(() => ({ send: spySend })); From 377bd7235131f363d7f5c871405c2e606dca3ce7 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 22 Sep 2023 13:46:08 +0200 Subject: [PATCH 12/14] Made public API enpoints check case-insensitive --- src/service/auth-service.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/service/auth-service.js b/src/service/auth-service.js index 49a0a65ee1..05c6c3093e 100644 --- a/src/service/auth-service.js +++ b/src/service/auth-service.js @@ -62,10 +62,15 @@ class AuthService { return false; } - return ( - this._authConfig.publicOperations.includes(`v0/${operationName}`) || - this._authConfig.publicOperations.includes(operationName) - ); + const lowerCaseOperationName = operationName.toLowerCase(); + + return this._authConfig.publicOperations.some((publicOperation) => { + const lowerCasePublicOperation = publicOperation.toLowerCase(); + return ( + lowerCasePublicOperation === `v0/${lowerCaseOperationName}` || + lowerCasePublicOperation === lowerCaseOperationName + ); + }); } /** From 3c7748ca3cbc7462834a1b8f0285ab0f1d6c8188 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 22 Sep 2023 13:52:57 +0200 Subject: [PATCH 13/14] Added hotfix suffix to the package version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4fdac68a57..429d10a630 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.16+hotfix.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.16+hotfix.1", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/package.json b/package.json index 307fadd425..deba86aace 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.16+hotfix.1", "description": "OTNode V6", "main": "index.js", "type": "module", From f9e412555f8b26553090f9907adda59921057bee Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 22 Sep 2023 15:01:17 +0200 Subject: [PATCH 14/14] Removed hotfix suffix in the package version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 429d10a630..4fdac68a57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.16+hotfix.1", + "version": "6.0.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.16+hotfix.1", + "version": "6.0.16", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/package.json b/package.json index deba86aace..307fadd425 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.16+hotfix.1", + "version": "6.0.16", "description": "OTNode V6", "main": "index.js", "type": "module",