From 386b109e613c64268d994802420b55c24a9bd761 Mon Sep 17 00:00:00 2001 From: Mihajlo Pavlovic Date: Fri, 20 Oct 2023 11:18:50 +0200 Subject: [PATCH 01/12] Transform telemtry-service to module (#2737) * Transform telemtry-service to module * Remove old telemetry files * Remove unneeded logging * Add telemetry module migration * Add telemetry module enabled check before startin it * Add ot-telemetry config * Rename telemetry migration class and file * Remove async from startTelemetryModule * Rename telemetry-service to ot-telemetry * Refine migration to only update user configuration telemetry settings * Remove old telemetry configuration * Fix version * Use getModuleConfiguration to get implementation config * Refactor sendTelemetryData to return a boolean for response success * Remove unneeded enable field * Fix onEvenetRecived -> onEventReceived typo * Fix ot-telemetry configuration for testnet and mainnet * Add unit test * Use getModuleConfiguration without argument * Revert package-lock changes * Update tests * Update tests * Format files using Prettier * Set better name for test --------- Co-authored-by: Mihajlo Pavlovic Co-authored-by: Mihajlo Pavlovic Co-authored-by: Nikola Todorovic --- config/config.json | 72 +++++++++++++------ ot-node.js | 54 +++++++++----- src/commands/common/send-telemetry-command.js | 60 +++++++++------- ...try-module-user-configuration-migration.js | 45 ++++++++++++ src/modules/base-module-manager.js | 2 +- src/modules/module-config-validation.js | 11 ++- .../telemetry/implementation/ot-telemetry.js | 29 ++++++++ .../telemetry/telemetry-module-manager.js | 29 ++++++++ src/service/telemetry-injection-service.js | 37 ---------- test/modules/telemetry/config.json | 17 +++++ test/modules/telemetry/telemetry.js | 51 +++++++++++++ 11 files changed, 303 insertions(+), 104 deletions(-) create mode 100644 src/migration/telemetry-module-user-configuration-migration.js create mode 100644 src/modules/telemetry/implementation/ot-telemetry.js create mode 100644 src/modules/telemetry/telemetry-module-manager.js delete mode 100644 src/service/telemetry-injection-service.js create mode 100644 test/modules/telemetry/config.json create mode 100644 test/modules/telemetry/telemetry.js diff --git a/config/config.json b/config/config.json index 9906ca1803..7b490510c4 100644 --- a/config/config.json +++ b/config/config.json @@ -166,17 +166,25 @@ } } } + }, + "telemetry": { + "enabled": true, + "implementation": { + "ot-telemetry": { + "enabled": true, + "package": "./telemetry/implementation/ot-telemetry.js", + "config": { + "sendTelemetryData": false, + "signalingServerUrl": "null" + } + } + } } }, "maximumAssertionSizeInKb": 2500, "commandExecutorVerboseLoggingEnabled": false, "appDataPath": "data", "logLevel": "info", - "telemetry": { - "enabled": true, - "sendTelemetryData": false, - "signalingServerUrl": "null" - }, "auth": { "ipBasedAuthEnabled": true, "tokenBasedAuthEnabled": false, @@ -301,17 +309,25 @@ "config": {} } } + }, + "telemetry": { + "enabled": true, + "implementation": { + "ot-telemetry": { + "enabled": true, + "package": "./telemetry/implementation/ot-telemetry.js", + "config": { + "sendTelemetryData": false, + "signalingServerUrl": "null" + } + } + } } }, "maximumAssertionSizeInKb": 2500, "commandExecutorVerboseLoggingEnabled": false, "appDataPath": "data", "logLevel": "trace", - "telemetry": { - "enabled": true, - "sendTelemetryData": false, - "signalingServerUrl": "null" - }, "auth": { "ipBasedAuthEnabled": true, "tokenBasedAuthEnabled": false, @@ -448,17 +464,25 @@ "config": {} } } + }, + "telemetry": { + "enabled": true, + "implementation": { + "ot-telemetry": { + "enabled": true, + "package": "./telemetry/implementation/ot-telemetry.js", + "config": { + "sendTelemetryData": true, + "signalingServerUrl": "https://testnet-signaling.origin-trail.network/signal" + } + } + } } }, "maximumAssertionSizeInKb": 2500, "commandExecutorVerboseLoggingEnabled": false, "appDataPath": "data", "logLevel": "trace", - "telemetry": { - "enabled": true, - "sendTelemetryData": true, - "signalingServerUrl": "https://testnet-signaling.origin-trail.network/signal" - }, "auth": { "ipBasedAuthEnabled": true, "tokenBasedAuthEnabled": false, @@ -596,17 +620,25 @@ "config": {} } } + }, + "telemetry": { + "enabled": true, + "implementation": { + "ot-telemetry": { + "enabled": true, + "package": "./telemetry/implementation/ot-telemetry.js", + "config": { + "sendTelemetryData": true, + "signalingServerUrl": "https://mainnet-signaling.origin-trail.network/signal" + } + } + } } }, "maximumAssertionSizeInKb": 2500, "commandExecutorVerboseLoggingEnabled": false, "appDataPath": "data", "logLevel": "trace", - "telemetry": { - "enabled": true, - "sendTelemetryData": true, - "signalingServerUrl": "https://mainnet-signaling.origin-trail.network/signal" - }, "auth": { "ipBasedAuthEnabled": true, "tokenBasedAuthEnabled": false, diff --git a/ot-node.js b/ot-node.js index fa68cfc2af..716669d52d 100644 --- a/ot-node.js +++ b/ot-node.js @@ -11,6 +11,7 @@ import OtnodeUpdateCommand from './src/commands/common/otnode-update-command.js' import OtAutoUpdater from './src/modules/auto-updater/implementation/ot-auto-updater.js'; import PullBlockchainShardingTableMigration from './src/migration/pull-sharding-table-migration.js'; import TripleStoreUserConfigurationMigration from './src/migration/triple-store-user-configuration-migration.js'; +import TelemetryModuleUserConfigurationMigration from './src/migration/telemetry-module-user-configuration-migration.js'; import PrivateAssetsMetadataMigration from './src/migration/private-assets-metadata-migration.js'; import ServiceAgreementsMetadataMigration from './src/migration/service-agreements-metadata-migration.js'; import RemoveAgreementStartEndTimeMigration from './src/migration/remove-agreement-start-end-time-migration.js'; @@ -36,6 +37,7 @@ class OTNode { await this.checkForUpdate(); await this.removeUpdateFile(); await this.executeTripleStoreUserConfigurationMigration(); + await this.executeTelemetryModuleUserConfigurationMigration(); this.logger.info(' ██████╗ ████████╗███╗ ██╗ ██████╗ ██████╗ ███████╗'); this.logger.info('██╔═══██╗╚══██╔══╝████╗ ██║██╔═══██╗██╔══██╗██╔════╝'); this.logger.info('██║ ██║ ██║ ██╔██╗ ██║██║ ██║██║ ██║█████╗'); @@ -65,11 +67,11 @@ class OTNode { await this.initializeCommandExecutor(); await this.initializeShardingTableService(); - await this.initializeTelemetryInjectionService(); await this.initializeBlockchainEventListenerService(); await this.initializeRouters(); await this.startNetworkModule(); + this.startTelemetryModule(); this.resumeCommandExecutor(); this.logger.info('Node is up and running!'); } @@ -275,6 +277,21 @@ class OTNode { await networkModuleManager.start(); } + startTelemetryModule() { + const telemetryModuleManager = this.container.resolve('telemetryModuleManager'); + const repositoryModuleManager = this.container.resolve('repositoryModuleManager'); + telemetryModuleManager.listenOnEvents((eventData) => { + repositoryModuleManager.createEventRecord( + eventData.operationId, + eventData.lastEvent, + eventData.timestamp, + eventData.value1, + eventData.value2, + eventData.value3, + ); + }); + } + async executePrivateAssetsMetadataMigration() { if ( process.env.NODE_ENV === NODE_ENVIRONMENTS.DEVELOPMENT || @@ -305,6 +322,25 @@ class OTNode { } } + async executeTelemetryModuleUserConfigurationMigration() { + if ( + process.env.NODE_ENV === NODE_ENVIRONMENTS.DEVELOPMENT || + process.env.NODE_ENV === NODE_ENVIRONMENTS.TEST + ) + return; + + const migration = new TelemetryModuleUserConfigurationMigration( + 'telemetryModuleUserConfigurationMigration', + this.logger, + this.config, + ); + if (!(await migration.migrationAlreadyExecuted())) { + await migration.migrate(); + this.logger.info('Node will now restart!'); + this.stop(1); + } + } + async executeTripleStoreUserConfigurationMigration() { if ( process.env.NODE_ENV === NODE_ENVIRONMENTS.DEVELOPMENT || @@ -474,22 +510,6 @@ class OTNode { } } - async initializeTelemetryInjectionService() { - if (this.config.telemetry.enabled) { - try { - const telemetryHubModuleManager = this.container.resolve( - 'telemetryInjectionService', - ); - telemetryHubModuleManager.initialize(); - this.logger.info('Telemetry Injection Service initialized successfully'); - } catch (e) { - this.logger.error( - `Telemetry hub module initialization failed. Error message: ${e.message}`, - ); - } - } - } - async removeUpdateFile() { const updateFilePath = this.fileService.getUpdateFilePath(); await this.fileService.removeFile(updateFilePath).catch((error) => { diff --git a/src/commands/common/send-telemetry-command.js b/src/commands/common/send-telemetry-command.js index 03fa2ff128..ada1aab635 100644 --- a/src/commands/common/send-telemetry-command.js +++ b/src/commands/common/send-telemetry-command.js @@ -1,4 +1,3 @@ -import axios from 'axios'; import { createRequire } from 'module'; import Command from '../command.js'; import { SEND_TELEMETRY_COMMAND_FREQUENCY_MINUTES } from '../../constants/constants.js'; @@ -11,9 +10,10 @@ class SendTelemetryCommand extends Command { super(ctx); this.logger = ctx.logger; this.config = ctx.config; - this.telemetryInjectionService = ctx.telemetryInjectionService; this.networkModuleManager = ctx.networkModuleManager; this.blockchainModuleManager = ctx.blockchainModuleManager; + this.repositoryModuleManager = ctx.repositoryModuleManager; + this.telemetryModuleManager = ctx.telemetryModuleManager; } /** @@ -21,35 +21,31 @@ class SendTelemetryCommand extends Command { * @param command */ async execute() { - if (!this.config.telemetry.enabled || !this.config.telemetry.sendTelemetryData) { + if ( + !this.config.modules.telemetry.enabled || + !this.telemetryModuleManager.getModuleConfiguration().sendTelemetryData + ) { return Command.empty(); } + try { - const events = await this.telemetryInjectionService.getUnpublishedEvents(); - const signalingMessage = { - nodeData: { - version: pjson.version, - identity: this.networkModuleManager.getPeerId().toB58String(), - hostname: this.config.hostname, - operational_wallet: this.blockchainModuleManager.getPublicKey(), - management_wallet: this.blockchainModuleManager.getManagementKey(), - triple_store: this.config.modules.tripleStore.defaultImplementation, - auto_update_enabled: this.config.modules.autoUpdater.enabled, - multiaddresses: this.networkModuleManager.getMultiaddrs(), - }, - events: events || [], - }; - const config = { - method: 'post', - url: this.config.telemetry.signalingServerUrl, - headers: { - 'Content-Type': 'application/json', - }, - data: JSON.stringify(signalingMessage), + const events = (await this.getUnpublishedEvents()) || []; + const nodeData = { + version: pjson.version, + identity: this.networkModuleManager.getPeerId().toB58String(), + hostname: this.config.hostname, + operational_wallet: this.blockchainModuleManager.getPublicKey(), + management_wallet: this.blockchainModuleManager.getManagementKey(), + triple_store: this.config.modules.tripleStore.defaultImplementation, + auto_update_enabled: this.config.modules.autoUpdater.enabled, + multiaddresses: this.networkModuleManager.getMultiaddrs(), }; - const response = await axios(config); - if (response.status === 200 && events?.length > 0) { - await this.telemetryInjectionService.removePublishedEvents(events); + const isDataSuccessfullySent = await this.telemetryModuleManager.sendTelemetryData( + nodeData, + events, + ); + if (isDataSuccessfullySent && events?.length > 0) { + await this.removePublishedEvents(events); } } catch (e) { await this.handleError(e); @@ -83,6 +79,16 @@ class SendTelemetryCommand extends Command { Object.assign(command, map); return command; } + + async getUnpublishedEvents() { + return this.repositoryModuleManager.getUnpublishedEvents(); + } + + async removePublishedEvents(events) { + const ids = events.map((event) => event.id); + + await this.repositoryModuleManager.destroyEvents(ids); + } } export default SendTelemetryCommand; diff --git a/src/migration/telemetry-module-user-configuration-migration.js b/src/migration/telemetry-module-user-configuration-migration.js new file mode 100644 index 0000000000..a49cc393af --- /dev/null +++ b/src/migration/telemetry-module-user-configuration-migration.js @@ -0,0 +1,45 @@ +import appRootPath from 'app-root-path'; +import path from 'path'; +import BaseMigration from './base-migration.js'; + +class TelemetryModuleUserConfigurationMigration extends BaseMigration { + async executeMigration() { + const configurationFolderPath = path.join(appRootPath.path, '..'); + const configurationFilePath = path.join( + configurationFolderPath, + this.config.configFilename, + ); + + const userConfiguration = await this.fileService.readFile(configurationFilePath, true); + + let newTelemetryConfig; + + if ('telemetry' in userConfiguration) { + const oldConfigTelemetry = userConfiguration.telemetry; + newTelemetryConfig = { + enabled: oldConfigTelemetry.enabled, + implementation: { + 'ot-telemetry': { + enabled: oldConfigTelemetry.enabled, + package: './telemetry/implementation/ot-telemetry.js', + config: { + sendTelemetryData: oldConfigTelemetry.sendTelemetryData, + signalingServerUrl: oldConfigTelemetry.signalingServerUrl, + }, + }, + }, + }; + + delete userConfiguration.telemetry; + userConfiguration.modules.telemetry = newTelemetryConfig; + + await this.fileService.writeContentsToFile( + configurationFolderPath, + this.config.configFilename, + JSON.stringify(userConfiguration, null, 4), + ); + } + } +} + +export default TelemetryModuleUserConfigurationMigration; diff --git a/src/modules/base-module-manager.js b/src/modules/base-module-manager.js index e96161999c..2bdc901d6a 100644 --- a/src/modules/base-module-manager.js +++ b/src/modules/base-module-manager.js @@ -91,7 +91,7 @@ class BaseModuleManager { delete this.handlers[name]; } - getModuleConfiguration(name) { + getModuleConfiguration(name = null) { return this.getImplementation(name).config; } } diff --git a/src/modules/module-config-validation.js b/src/modules/module-config-validation.js index 4eaaf02b42..fc2cd00619 100644 --- a/src/modules/module-config-validation.js +++ b/src/modules/module-config-validation.js @@ -8,9 +8,12 @@ class ModuleConfigValidation { validateModule(name, config) { this.validateRequiredModule(name, config); - const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1); - this[`validate${capitalizedName}`](config); + if (typeof this[`validate${capitalizedName}`] === 'function') { + this[`validate${capitalizedName}`](config); + } else { + throw Error(`Missing validation for ${capitalizedName}`); + } } validateAutoUpdater() { @@ -68,6 +71,10 @@ class ModuleConfigValidation { this.logger.warn(message); } } + + validateTelemetry() { + return true; + } } export default ModuleConfigValidation; diff --git a/src/modules/telemetry/implementation/ot-telemetry.js b/src/modules/telemetry/implementation/ot-telemetry.js new file mode 100644 index 0000000000..c4460c7572 --- /dev/null +++ b/src/modules/telemetry/implementation/ot-telemetry.js @@ -0,0 +1,29 @@ +import axios from 'axios'; + +class OTTelemetry { + async initialize(config, logger) { + this.config = config; + this.logger = logger; + } + + listenOnEvents(eventEmitter, onEventReceived) { + return eventEmitter.on('operation_status_changed', onEventReceived); + } + + async sendTelemetryData(nodeData, events) { + const signalingMessage = { nodeData, events }; + const config = { + method: 'post', + url: this.config.signalingServerUrl, + headers: { + 'Content-Type': 'application/json', + }, + data: JSON.stringify(signalingMessage), + }; + const response = await axios(config); + const isSuccess = response.status === 200; + return isSuccess; + } +} + +export default OTTelemetry; diff --git a/src/modules/telemetry/telemetry-module-manager.js b/src/modules/telemetry/telemetry-module-manager.js new file mode 100644 index 0000000000..f5ef04377e --- /dev/null +++ b/src/modules/telemetry/telemetry-module-manager.js @@ -0,0 +1,29 @@ +import BaseModuleManager from '../base-module-manager.js'; + +class TelemetryModuleManager extends BaseModuleManager { + constructor(ctx) { + super(ctx); + this.eventEmitter = ctx.eventEmitter; + } + + getName() { + return 'telemetry'; + } + + listenOnEvents(onEventReceived) { + if (this.config.modules.telemetry.enabled && this.initialized) { + return this.getImplementation().module.listenOnEvents( + this.eventEmitter, + onEventReceived, + ); + } + } + + async sendTelemetryData(nodeData, events) { + if (this.initialized) { + return this.getImplementation().module.sendTelemetryData(nodeData, events); + } + } +} + +export default TelemetryModuleManager; diff --git a/src/service/telemetry-injection-service.js b/src/service/telemetry-injection-service.js deleted file mode 100644 index be59e6ca4f..0000000000 --- a/src/service/telemetry-injection-service.js +++ /dev/null @@ -1,37 +0,0 @@ -class TelemetryInjectionService { - constructor(ctx) { - this.logger = ctx.logger; - this.config = ctx.config; - this.eventEmitter = ctx.eventEmitter; - this.repositoryModuleManager = ctx.repositoryModuleManager; - } - - initialize() { - this.listenOnEvents(); - } - - listenOnEvents() { - this.eventEmitter.on('operation_status_changed', (eventData) => { - this.repositoryModuleManager.createEventRecord( - eventData.operationId, - eventData.lastEvent, - eventData.timestamp, - eventData.value1, - eventData.value2, - eventData.value3, - ); - }); - } - - async getUnpublishedEvents() { - return this.repositoryModuleManager.getUnpublishedEvents(); - } - - async removePublishedEvents(events) { - const ids = events.map((event) => event.id); - - await this.repositoryModuleManager.destroyEvents(ids); - } -} - -export default TelemetryInjectionService; diff --git a/test/modules/telemetry/config.json b/test/modules/telemetry/config.json new file mode 100644 index 0000000000..66c5eddea5 --- /dev/null +++ b/test/modules/telemetry/config.json @@ -0,0 +1,17 @@ +{ + "modules": { + "telemetry": { + "enabled": true, + "implementation": { + "ot-telemetry": { + "enabled": true, + "package": "./telemetry/implementation/ot-telemetry.js", + "config": { + "sendTelemetryData": false, + "signalingServerUrl": "null" + } + } + } + } + } +} diff --git a/test/modules/telemetry/telemetry.js b/test/modules/telemetry/telemetry.js new file mode 100644 index 0000000000..6a7c037fa7 --- /dev/null +++ b/test/modules/telemetry/telemetry.js @@ -0,0 +1,51 @@ +import { readFile } from 'fs/promises'; +import { describe, it, before } from 'mocha'; +import { expect, assert } from 'chai'; +import Logger from '../../../src/logger/logger.js'; +import TelemetryModuleManager from '../../../src/modules/telemetry/telemetry-module-manager.js'; + +let logger; +let telemetryModuleManager; +const config = JSON.parse(await readFile('./test/modules/telemetry/config.json')); + +describe('Telemetry module', () => { + before('Initialize logger', () => { + logger = new Logger('trace'); + logger.info = () => {}; + }); + + describe('Handle received events', () => { + it('should call onEventReceived when event is emitted', async () => { + const eventEmitter = { + eventListeners: {}, + + on(eventName, callback) { + if (!this.eventListeners[eventName]) { + this.eventListeners[eventName] = []; + } + this.eventListeners[eventName].push(callback); + }, + + emit(eventName, ...args) { + if (this.eventListeners[eventName]) { + this.eventListeners[eventName].forEach((callback) => callback(...args)); + } + }, + }; + + let callbackCalled = false; + + function onEventReceived() { + callbackCalled = true; + } + + telemetryModuleManager = new TelemetryModuleManager({ config, logger, eventEmitter }); + await telemetryModuleManager.initialize(); + telemetryModuleManager.listenOnEvents(onEventReceived); + + eventEmitter.emit('operation_status_changed'); + + assert(expect(callbackCalled).to.be.true); + }); + }); +}); From 3f610e0962ec584484da45eb5873fa51e7c5b1a6 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 3 Nov 2023 15:25:19 +0100 Subject: [PATCH 02/12] Added error handling for commits/proofs commands, added error handling when proof is empty, removed duplicated SA record update, increased tx polling timeout to 120 seconds --- .../protocols/common/submit-commit-command.js | 7 ++++--- .../protocols/common/submit-proofs-command.js | 20 +++++++++++++++---- .../v1-0-0-handle-store-request-command.js | 16 --------------- src/constants/constants.js | 8 ++++---- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/commands/protocols/common/submit-commit-command.js b/src/commands/protocols/common/submit-commit-command.js index 7c9cfba13a..f3452eb707 100644 --- a/src/commands/protocols/common/submit-commit-command.js +++ b/src/commands/protocols/common/submit-commit-command.js @@ -49,9 +49,10 @@ class SubmitCommitCommand extends Command { stateIndex, ); if (alreadySubmitted) { - this.logger.trace( - `Commit already submitted for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}`, - ); + const errorMessage = `Commit already submitted for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}`; + this.logger.trace(errorMessage); + + this.handleError(operationId, errorMessage, this.errorType, true); return Command.empty(); } diff --git a/src/commands/protocols/common/submit-proofs-command.js b/src/commands/protocols/common/submit-proofs-command.js index 8348480f84..3b724405bd 100644 --- a/src/commands/protocols/common/submit-proofs-command.js +++ b/src/commands/protocols/common/submit-proofs-command.js @@ -66,7 +66,10 @@ class SubmitProofsCommand extends Command { ); if (!assertion.length) { - this.logger.trace(`Assertion with id: ${assertionId} not found in triple store.`); + const errorMessage = `Assertion with id: ${assertionId} not found in triple store.`; + this.logger.trace(errorMessage); + + this.handleError(operationId, errorMessage, this.errorType, true); return Command.empty(); } @@ -97,9 +100,18 @@ class SubmitProofsCommand extends Command { stateIndex, ); if (alreadySubmitted) { - this.logger.trace( - `Proofs already submitted for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}`, - ); + const errorMessage = `Proofs already submitted for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}`; + + this.logger.trace(errorMessage); + this.handleError(operationId, errorMessage, this.errorType, true); + return Command.empty(); + } + + if (proof.length === 0) { + const errorMessage = `Error during Merkle Proof calculation for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}, proof cannot be empty`; + + this.logger.trace(errorMessage); + this.handleError(operationId, errorMessage, this.errorType, true); return Command.empty(); } diff --git a/src/commands/protocols/publish/receiver/v1.0.0/v1-0-0-handle-store-request-command.js b/src/commands/protocols/publish/receiver/v1.0.0/v1-0-0-handle-store-request-command.js index 0caa031c63..5e82bd6178 100644 --- a/src/commands/protocols/publish/receiver/v1.0.0/v1-0-0-handle-store-request-command.js +++ b/src/commands/protocols/publish/receiver/v1.0.0/v1-0-0-handle-store-request-command.js @@ -83,22 +83,6 @@ class HandleStoreRequestCommand extends HandleProtocolMessageCommand { stateIndex, ); - await this.repositoryModuleManager.updateServiceAgreementRecord( - blockchain, - contract, - tokenId, - agreementId, - agreementData.startTime, - agreementData.epochsNumber, - agreementData.epochLength, - agreementData.scoreFunctionId, - agreementData.proofWindowOffsetPerc, - hashFunctionId, - keyword, - assertionId, - stateIndex, - ); - await this.operationIdService.updateOperationIdStatus( operationId, OPERATION_ID_STATUS.PUBLISH.PUBLISH_LOCAL_STORE_END, diff --git a/src/constants/constants.js b/src/constants/constants.js index 3fff5854e4..285441faee 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -21,7 +21,7 @@ export const COMMIT_BLOCK_DURATION_IN_BLOCKS = 5; export const COMMITS_DELAY_BETWEEN_NODES_IN_BLOCKS = 2; -export const TRANSACTION_POLLING_TIMEOUT_MILLIS = 50 * 1000; +export const TRANSACTION_POLLING_TIMEOUT_MILLIS = 120 * 1000; export const SOLIDITY_ERROR_STRING_PREFIX = '0x08c379a0'; @@ -167,9 +167,9 @@ export const DEFAULT_COMMAND_REPEAT_INTERVAL_IN_MILLS = 5000; // 5 seconds export const DEFAULT_COMMAND_DELAY_IN_MILLS = 60 * 1000; // 60 seconds export const COMMAND_RETRIES = { - SUBMIT_COMMIT: 3, - SUBMIT_UPDATE_COMMIT: 3, - SUBMIT_PROOFS: 3, + SUBMIT_COMMIT: 0, + SUBMIT_UPDATE_COMMIT: 0, + SUBMIT_PROOFS: 0, }; export const WEBSOCKET_PROVIDER_OPTIONS = { From 4bcbf68f8d67991f4b8786340d27c07556a8e8e1 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 3 Nov 2023 15:25:57 +0100 Subject: [PATCH 03/12] Bumped version to 6.0.17 --- 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..b9088bc25c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.17", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/package.json b/package.json index 307fadd425..a8218caca2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.16", + "version": "6.0.17", "description": "OTNode V6", "main": "index.js", "type": "module", From d556a6d5af63ceff171e2d7ffe8069b456ef71e4 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 3 Nov 2023 15:33:01 +0100 Subject: [PATCH 04/12] Added missing awaits for handleError --- src/commands/protocols/common/submit-commit-command.js | 2 +- src/commands/protocols/common/submit-proofs-command.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/commands/protocols/common/submit-commit-command.js b/src/commands/protocols/common/submit-commit-command.js index f3452eb707..b2312b72ee 100644 --- a/src/commands/protocols/common/submit-commit-command.js +++ b/src/commands/protocols/common/submit-commit-command.js @@ -52,7 +52,7 @@ class SubmitCommitCommand extends Command { const errorMessage = `Commit already submitted for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}`; this.logger.trace(errorMessage); - this.handleError(operationId, errorMessage, this.errorType, true); + await this.handleError(operationId, errorMessage, this.errorType, true); return Command.empty(); } diff --git a/src/commands/protocols/common/submit-proofs-command.js b/src/commands/protocols/common/submit-proofs-command.js index 3b724405bd..2e44f23ba2 100644 --- a/src/commands/protocols/common/submit-proofs-command.js +++ b/src/commands/protocols/common/submit-proofs-command.js @@ -69,7 +69,7 @@ class SubmitProofsCommand extends Command { const errorMessage = `Assertion with id: ${assertionId} not found in triple store.`; this.logger.trace(errorMessage); - this.handleError(operationId, errorMessage, this.errorType, true); + await this.handleError(operationId, errorMessage, this.errorType, true); return Command.empty(); } @@ -101,17 +101,17 @@ class SubmitProofsCommand extends Command { ); if (alreadySubmitted) { const errorMessage = `Proofs already submitted for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}`; - this.logger.trace(errorMessage); - this.handleError(operationId, errorMessage, this.errorType, true); + + await this.handleError(operationId, errorMessage, this.errorType, true); return Command.empty(); } if (proof.length === 0) { const errorMessage = `Error during Merkle Proof calculation for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}, proof cannot be empty`; - this.logger.trace(errorMessage); - this.handleError(operationId, errorMessage, this.errorType, true); + + await this.handleError(operationId, errorMessage, this.errorType, true); return Command.empty(); } From 87fdddc2a5651f47fd3238284186cc57ca05e88a Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 3 Nov 2023 18:21:55 +0100 Subject: [PATCH 05/12] Implemented Fallback RPC Provider, changed the way of handling commit/proof errors --- config/config.json | 8 +- .../protocols/common/submit-commit-command.js | 9 ++- .../protocols/common/submit-proofs-command.js | 9 ++- src/constants/constants.js | 8 ++ .../blockchain/implementation/web3-service.js | 73 +++++++++---------- 5 files changed, 64 insertions(+), 43 deletions(-) diff --git a/config/config.json b/config/config.json index 7b490510c4..e16f1e509d 100644 --- a/config/config.json +++ b/config/config.json @@ -131,7 +131,10 @@ "config": { "networkId": "otp::testnet", "hubContractAddress": "0x707233a55bD035C6Bc732196CA4dbffa63CbA169", - "rpcEndpoints": ["https://lofar-tm-rpc.origin-trail.network"], + "rpcEndpoints": [ + "https://lofar-tm-rpc.origin-trail.network", + "https://lofar.origintrail.network/" + ], "initialStakeAmount": 50000, "initialAskAmount": 2 } @@ -585,7 +588,8 @@ "hubContractAddress": "0x5fA7916c48Fe6D5F1738d12Ad234b78c90B4cAdA", "rpcEndpoints": [ "https://astrosat-parachain-rpc.origin-trail.network", - "https://astrosat.origintrail.network/" + "https://astrosat.origintrail.network/", + "https://astrosat-2.origintrail.network/" ] } } diff --git a/src/commands/protocols/common/submit-commit-command.js b/src/commands/protocols/common/submit-commit-command.js index b2312b72ee..e4871b4465 100644 --- a/src/commands/protocols/common/submit-commit-command.js +++ b/src/commands/protocols/common/submit-commit-command.js @@ -52,7 +52,12 @@ class SubmitCommitCommand extends Command { const errorMessage = `Commit already submitted for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}`; this.logger.trace(errorMessage); - await this.handleError(operationId, errorMessage, this.errorType, true); + this.operationIdService.emitChangeEvent( + OPERATION_ID_STATUS.COMMIT_PROOF.SUBMIT_COMMIT_END, + operationId, + agreementId, + epoch, + ); return Command.empty(); } @@ -66,7 +71,7 @@ class SubmitCommitCommand extends Command { epoch, stateIndex, (result) => { - if (result?.error) { + if (result?.error && !result.error.includes('NodeAlreadySubmittedCommit')) { reject(result.error); } resolve(); diff --git a/src/commands/protocols/common/submit-proofs-command.js b/src/commands/protocols/common/submit-proofs-command.js index 2e44f23ba2..e59e4ace28 100644 --- a/src/commands/protocols/common/submit-proofs-command.js +++ b/src/commands/protocols/common/submit-proofs-command.js @@ -103,7 +103,12 @@ class SubmitProofsCommand extends Command { const errorMessage = `Proofs already submitted for blockchain: ${blockchain} agreement id: ${agreementId}, epoch: ${epoch}, state index: ${stateIndex}`; this.logger.trace(errorMessage); - await this.handleError(operationId, errorMessage, this.errorType, true); + this.operationIdService.emitChangeEvent( + OPERATION_ID_STATUS.COMMIT_PROOF.SUBMIT_PROOFS_END, + operationId, + agreementId, + epoch, + ); return Command.empty(); } @@ -127,7 +132,7 @@ class SubmitProofsCommand extends Command { leaf, stateIndex, (result) => { - if (result?.error) { + if (result?.error && !result.error.includes('NodeAlreadyRewarded')) { reject(result.error); } resolve(); diff --git a/src/constants/constants.js b/src/constants/constants.js index 285441faee..ba1d328b3c 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -1,5 +1,13 @@ import { BigNumber } from 'ethers'; +export const WS_RPC_PROVIDER_PRIORITY = 2; + +export const HTTP_RPC_PROVIDER_PRIORITY = 1; + +export const FALLBACK_PROVIDER_QUORUM = 1; + +export const RPC_PROVIDER_STALL_TIMEOUT = 60 * 1000; + export const UINT256_MAX_BN = BigNumber.from(2).pow(256).sub(1); export const UINT32_MAX_BN = BigNumber.from(2).pow(32).sub(1); diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index 49632f426f..c3fbb03f68 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -15,6 +15,10 @@ import { TRANSACTION_POLLING_TIMEOUT_MILLIS, TRANSACTION_CONFIRMATIONS, BLOCK_TIME_MILLIS, + WS_RPC_PROVIDER_PRIORITY, + HTTP_RPC_PROVIDER_PRIORITY, + FALLBACK_PROVIDER_QUORUM, + RPC_PROVIDER_STALL_TIMEOUT, } from '../../../constants/constants.js'; const require = createRequire(import.meta.url); @@ -53,7 +57,6 @@ class Web3Service { this.config = config; this.logger = logger; - this.rpcNumber = 0; this.initializeTransactionQueue(TRANSACTION_QUEUE_CONCURRENCY); await this.initializeWeb3(); this.startBlock = await this.getBlockNumber(); @@ -89,37 +92,44 @@ class Web3Service { } async initializeWeb3() { - let tries = 0; - let isRpcConnected = false; - while (!isRpcConnected) { - if (tries >= this.config.rpcEndpoints.length) { - throw Error('RPC initialization failed'); - } - + const rpcProviders = []; + for (const rpcEndpoint of this.config.rpcEndpoints) { try { - if (this.config.rpcEndpoints[this.rpcNumber].startsWith('ws')) { - this.provider = new ethers.providers.WebSocketProvider( - this.config.rpcEndpoints[this.rpcNumber], - ); + if (rpcEndpoint.startsWith('ws')) { + rpcProviders.push({ + provider: new ethers.providers.WebSocketProvider(rpcEndpoint), + priority: WS_RPC_PROVIDER_PRIORITY, + weight: 1, + stallTimeout: RPC_PROVIDER_STALL_TIMEOUT, + }); } else { - this.provider = new ethers.providers.JsonRpcProvider( - this.config.rpcEndpoints[this.rpcNumber], - ); + rpcProviders.push({ + provider: new ethers.providers.JsonRpcProvider(rpcEndpoint), + priority: HTTP_RPC_PROVIDER_PRIORITY, + weight: 1, + stallTimeout: RPC_PROVIDER_STALL_TIMEOUT, + }); } - // eslint-disable-next-line no-await-in-loop - await this.providerReady(); - isRpcConnected = true; + this.logger.debug(`Connected to the blockchain RPC: ${rpcEndpoint}.`); } catch (e) { - this.logger.warn( - `Unable to connect to blockchain rpc : ${ - this.config.rpcEndpoints[this.rpcNumber] - }.`, - ); - tries += 1; - this.rpcNumber = (this.rpcNumber + 1) % this.config.rpcEndpoints.length; + this.logger.warn(`Unable to connect to the blockchain RPC: ${rpcEndpoint}.`); } } + try { + this.provider = new ethers.providers.FallbackProvider( + rpcProviders, + FALLBACK_PROVIDER_QUORUM, + ); + } catch (e) { + throw Error( + `RPC Fallback Provider initialization failed. Fallback Provider quorum: ${FALLBACK_PROVIDER_QUORUM}. Error: ${e.message}.`, + ); + } + + // eslint-disable-next-line no-await-in-loop + await this.providerReady(); + this.wallet = new ethers.Wallet(this.getPrivateKey(), this.provider); } @@ -164,9 +174,6 @@ class Web3Service { }); this.logger.info(`Contracts initialized`); - this.logger.debug( - `Connected to blockchain rpc : ${this.config.rpcEndpoints[this.rpcNumber]}.`, - ); await this.logBalances(); } @@ -925,12 +932,6 @@ class Web3Service { } async restartService() { - this.rpcNumber = (this.rpcNumber + 1) % this.config.rpcEndpoints.length; - this.logger.warn( - `There was an issue with current blockchain rpc. Connecting to ${ - this.config.rpcEndpoints[this.rpcNumber] - }`, - ); await this.initializeWeb3(); await this.initializeContracts(); } @@ -942,9 +943,7 @@ class Web3Service { } catch (rpcError) { isRpcError = true; this.logger.warn( - `Unable to execute smart contract function ${functionName} using blockchain rpc : ${ - this.config.rpcEndpoints[this.rpcNumber] - }.`, + `Unable to execute smart contract function ${functionName} using Fallback RPC Provider.`, ); await this.restartService(); } From b51d56a0c90d952966e17a6950fdb01a0aebb02d Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 3 Nov 2023 18:37:48 +0100 Subject: [PATCH 06/12] Bumped version to 6.0.18 --- 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 b9088bc25c..c4bc3b26fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.17", + "version": "6.0.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.17", + "version": "6.0.18", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/package.json b/package.json index a8218caca2..11eec6e27d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.17", + "version": "6.0.18", "description": "OTNode V6", "main": "index.js", "type": "module", From 7bc8b18681f8ff93051f1bcb598b8f9210eb350d Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 3 Nov 2023 19:00:03 +0100 Subject: [PATCH 07/12] Fixed error message check --- package-lock.json | 4 ++-- package.json | 2 +- src/commands/protocols/common/submit-commit-command.js | 5 ++++- src/commands/protocols/common/submit-proofs-command.js | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index c4bc3b26fd..52c9e6913a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.18", + "version": "6.0.18+hotfix.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.18", + "version": "6.0.18+hotfix.1", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/package.json b/package.json index 11eec6e27d..3880e01230 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.18", + "version": "6.0.18+hotfix.1", "description": "OTNode V6", "main": "index.js", "type": "module", diff --git a/src/commands/protocols/common/submit-commit-command.js b/src/commands/protocols/common/submit-commit-command.js index e4871b4465..e8534b0ee2 100644 --- a/src/commands/protocols/common/submit-commit-command.js +++ b/src/commands/protocols/common/submit-commit-command.js @@ -71,7 +71,10 @@ class SubmitCommitCommand extends Command { epoch, stateIndex, (result) => { - if (result?.error && !result.error.includes('NodeAlreadySubmittedCommit')) { + if ( + result?.error && + !result.error.message.includes('NodeAlreadySubmittedCommit') + ) { reject(result.error); } resolve(); diff --git a/src/commands/protocols/common/submit-proofs-command.js b/src/commands/protocols/common/submit-proofs-command.js index e59e4ace28..7e26edf5c5 100644 --- a/src/commands/protocols/common/submit-proofs-command.js +++ b/src/commands/protocols/common/submit-proofs-command.js @@ -132,7 +132,7 @@ class SubmitProofsCommand extends Command { leaf, stateIndex, (result) => { - if (result?.error && !result.error.includes('NodeAlreadyRewarded')) { + if (result?.error && !result.error.message.includes('NodeAlreadyRewarded')) { reject(result.error); } resolve(); From 893f310be41ca592bb5c114e4fcc738c81bc6e95 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 6 Nov 2023 14:35:13 +0100 Subject: [PATCH 08/12] Fix KA validation on publish/update to check if assertion ID is 0x0, fixed FallbackProvider initialization, added additional blockchain calls/txs logs --- package-lock.json | 4 +- package.json | 2 +- src/commands/common/validate-asset-command.js | 9 +- .../blockchain/implementation/web3-service.js | 181 +++++++++++++----- 4 files changed, 146 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52c9e6913a..97940db30e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.18+hotfix.1", + "version": "6.0.18+hotfix.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.18+hotfix.1", + "version": "6.0.18+hotfix.2", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/package.json b/package.json index 3880e01230..0bd14c103a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.18+hotfix.1", + "version": "6.0.18+hotfix.2", "description": "OTNode V6", "main": "index.js", "type": "module", diff --git a/src/commands/common/validate-asset-command.js b/src/commands/common/validate-asset-command.js index 458396d630..317a45cdb6 100644 --- a/src/commands/common/validate-asset-command.js +++ b/src/commands/common/validate-asset-command.js @@ -1,5 +1,10 @@ import Command from '../command.js'; -import { ERROR_TYPE, OPERATION_ID_STATUS, LOCAL_STORE_TYPES } from '../../constants/constants.js'; +import { + ERROR_TYPE, + OPERATION_ID_STATUS, + LOCAL_STORE_TYPES, + ZERO_BYTES32, +} from '../../constants/constants.js'; class ValidateAssetCommand extends Command { constructor(ctx) { @@ -43,7 +48,7 @@ class ValidateAssetCommand extends Command { tokenId, ); } - if (!blockchainAssertionId) { + if (!blockchainAssertionId || blockchainAssertionId === ZERO_BYTES32) { return Command.retry(); } const cachedData = await this.operationIdService.getCachedOperationIdData(operationId); diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index c3fbb03f68..d72c91c112 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -61,6 +61,7 @@ class Web3Service { await this.initializeWeb3(); this.startBlock = await this.getBlockNumber(); await this.initializeContracts(); + this.initializeProviderDebugging(); } initializeTransactionQueue(concurrency) { @@ -92,24 +93,26 @@ class Web3Service { } async initializeWeb3() { - const rpcProviders = []; + const providers = []; for (const rpcEndpoint of this.config.rpcEndpoints) { + const isWebSocket = rpcEndpoint.startsWith('ws'); + const Provider = isWebSocket + ? ethers.providers.WebSocketProvider + : ethers.providers.JsonRpcProvider; + const priority = isWebSocket ? WS_RPC_PROVIDER_PRIORITY : HTTP_RPC_PROVIDER_PRIORITY; + try { - if (rpcEndpoint.startsWith('ws')) { - rpcProviders.push({ - provider: new ethers.providers.WebSocketProvider(rpcEndpoint), - priority: WS_RPC_PROVIDER_PRIORITY, - weight: 1, - stallTimeout: RPC_PROVIDER_STALL_TIMEOUT, - }); - } else { - rpcProviders.push({ - provider: new ethers.providers.JsonRpcProvider(rpcEndpoint), - priority: HTTP_RPC_PROVIDER_PRIORITY, - weight: 1, - stallTimeout: RPC_PROVIDER_STALL_TIMEOUT, - }); - } + const provider = new Provider(rpcEndpoint); + // eslint-disable-next-line no-await-in-loop + await provider.getNetwork(); + + providers.push({ + provider, + priority, + weight: 1, + stallTimeout: RPC_PROVIDER_STALL_TIMEOUT, + }); + this.logger.debug(`Connected to the blockchain RPC: ${rpcEndpoint}.`); } catch (e) { this.logger.warn(`Unable to connect to the blockchain RPC: ${rpcEndpoint}.`); @@ -118,22 +121,24 @@ class Web3Service { try { this.provider = new ethers.providers.FallbackProvider( - rpcProviders, + providers, FALLBACK_PROVIDER_QUORUM, ); + + // eslint-disable-next-line no-await-in-loop + await this.providerReady(); } catch (e) { throw Error( `RPC Fallback Provider initialization failed. Fallback Provider quorum: ${FALLBACK_PROVIDER_QUORUM}. Error: ${e.message}.`, ); } - // eslint-disable-next-line no-await-in-loop - await this.providerReady(); - this.wallet = new ethers.Wallet(this.getPrivateKey(), this.provider); } async initializeContracts() { + this.contractAddresses = {}; + this.logger.info( `Initializing contracts with hub contract address: ${this.config.hubContractAddress}`, ); @@ -142,6 +147,7 @@ class Web3Service { ABIs.Hub, this.wallet, ); + this.contractAddresses[this.config.hubContractAddress] = this.HubContract; const contractsArray = await this.callContractFunction( this.HubContract, @@ -178,12 +184,81 @@ class Web3Service { await this.logBalances(); } + initializeProviderDebugging() { + this.provider.on('debug', (info) => { + switch (info.request.method) { + case 'call': + if (info.backend.error) { + const contractInstance = this.contractAddresses[info.request.params.to]; + const inputData = info.request.params.data; + const decodedInputData = this._decodeInputData( + inputData, + contractInstance.interface, + ); + const decodedErrorData = this._decodeErrorData( + info.backend.error, + contractInstance.interface, + ); + + this.logger.debug( + `${decodedInputData} call has failed; ` + + `Error: ${decodedErrorData}; ` + + `RPC: ${info.backend.provider.connection.url}.`, + ); + } + break; + case 'estimateGas': { + const contractInstance = this.contractAddresses[info.request.params.to]; + const inputData = info.request.params.data; + let decodedInputData; + if (info.backend.result || info.backend.result === null) { + decodedInputData = this._decodeInputData( + inputData, + contractInstance.interface, + ); + const decodedResultData = this._decodeResultData( + inputData.slice(0, 10), + info.backend.result, + contractInstance.interface, + ); + + this.logger.debug( + `${decodedInputData} transaction has been successfully executed; ` + + `Result: ${decodedResultData} ` + + `RPC: ${info.backend.provider.connection.url}.`, + ); + } else if (info.backend.error) { + decodedInputData = this._decodeInputData( + inputData, + contractInstance.interface, + ); + const decodedErrorData = this._decodeErrorData( + info.backend.error, + contractInstance.interface, + ); + + this.logger.debug( + `${decodedInputData} transaction has failed; ` + + `Error: ${decodedErrorData}; ` + + `RPC: ${info.backend.provider.connection.url}.`, + ); + } + break; + } + default: + break; + } + }); + } + initializeAssetStorageContract(assetStorageAddress) { this.assetStorageContracts[assetStorageAddress.toLowerCase()] = new ethers.Contract( assetStorageAddress, ABIs.ContentAssetStorage, this.wallet, ); + this.contractAddresses[assetStorageAddress] = + this.assetStorageContracts[assetStorageAddress.toLowerCase()]; } initializeScoringContract(id, contractAddress) { @@ -195,6 +270,7 @@ class Web3Service { ABIs[contractName], this.wallet, ); + this.contractAddresses[contractAddress] = this.scoringFunctionsContracts[id]; } else { this.logger.trace( `Skipping initialisation of contract with id: ${id}, address: ${contractAddress}`, @@ -209,6 +285,7 @@ class Web3Service { ABIs[contractName], this.wallet, ); + this.contractAddresses[contractAddress] = this[`${contractName}Contract`]; } else { this.logger.trace( `Skipping initialisation of contract: ${contractName}, address: ${contractAddress}`, @@ -343,8 +420,12 @@ class Web3Service { // eslint-disable-next-line no-await-in-loop result = await contractInstance[functionName](...args); } catch (error) { + const decodedErrorData = this._decodeErrorData(error, contractInstance.interface); // eslint-disable-next-line no-await-in-loop - await this.handleError(error, functionName); + await this.handleError( + Error(`Call failed, reason: ${decodedErrorData}`), + functionName, + ); } } @@ -363,19 +444,19 @@ class Web3Service { /* eslint-disable no-await-in-loop */ gasLimit = await contractInstance.estimateGas[functionName](...args); } catch (error) { - const decodedReturnData = this._decodeReturnData(error, contractInstance.interface); + const decodedErrorData = this._decodeErrorData(error, contractInstance.interface); await this.handleError( - Error(`gas estimation failed, reason: ${decodedReturnData}`), + Error(`Gas estimation failed, reason: ${decodedErrorData}`), 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()}`, + this.logger.debug( + `Sending signed transaction ${functionName} to the blockchain ` + + `with gas limit: ${gasLimit.toString()} and gasPrice ${gasPrice.toString()}. ` + + `Transaction queue length: ${this.getTransactionQueueLength()}.`, ); try { @@ -393,23 +474,17 @@ class Web3Service { 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: ${decodedReturnData}`, - ); + const decodedErrorData = this._decodeErrorData(error, contractInstance.interface); if ( !transactionRetried && (error.message.includes(`timeout exceeded`) || error.message.includes(`Pool(TooLowPriority`)) ) { gasPrice = Math.ceil(gasPrice * 1.2); - this.logger.warn( - `Retrying to execute smart contract function ${functionName} with gasPrice: ${gasPrice}`, - ); transactionRetried = true; } else { await this.handleError( - Error(`transaction reverted, reason: ${decodedReturnData}`), + Error(`Transaction reverted, reason: ${decodedErrorData}`), functionName, ); } @@ -418,7 +493,7 @@ class Web3Service { return result; } - _getReturnData(error) { + _getErrorData(error) { let nestedError = error; while (nestedError && nestedError.error) { nestedError = nestedError.error; @@ -442,23 +517,31 @@ class Web3Service { return returnData; } - _decodeReturnData(evmError, contractInterface) { - let returnData; + _decodeInputData(inputData, contractInterface) { + if (inputData === ZERO_PREFIX) { + return 'Empty input data.'; + } + + return contractInterface.decodeFunctionData(inputData.slice(0, 10), inputData); + } + + _decodeErrorData(evmError, contractInterface) { + let errorData; try { - returnData = this._getReturnData(evmError); + errorData = this._getErrorData(evmError); } catch (error) { return error.message; } // Handle empty error data - if (returnData === ZERO_PREFIX) { + if (errorData === 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); + if (errorData.startsWith(SOLIDITY_ERROR_STRING_PREFIX)) { + const encodedReason = errorData.slice(SOLIDITY_ERROR_STRING_PREFIX.length); try { return ethers.utils.defaultAbiCoder.decode(['string'], `0x${encodedReason}`)[0]; } catch (error) { @@ -467,8 +550,8 @@ class Web3Service { } // Handle solidity panic code - if (returnData.startsWith(SOLIDITY_PANIC_CODE_PREFIX)) { - const encodedReason = returnData.slice(SOLIDITY_PANIC_CODE_PREFIX.length); + if (errorData.startsWith(SOLIDITY_PANIC_CODE_PREFIX)) { + const encodedReason = errorData.slice(SOLIDITY_PANIC_CODE_PREFIX.length); let code; try { [code] = ethers.utils.defaultAbiCoder.decode(['uint256'], `0x${encodedReason}`); @@ -481,7 +564,7 @@ class Web3Service { // Try parsing a custom error using the contract ABI try { - const decodedCustomError = contractInterface.parseError(returnData); + const decodedCustomError = contractInterface.parseError(errorData); const formattedArgs = decodedCustomError.errorFragment.inputs .map((input, i) => { const argName = input.name; @@ -495,6 +578,14 @@ class Web3Service { } } + _decodeResultData(fragment, resultData, contractInterface) { + if (resultData === ZERO_PREFIX) { + return 'Empty input data.'; + } + + return contractInterface.decodeFunctionResult(fragment, resultData); + } + _formatCustomErrorArgument(value) { if (value === null || value === undefined) { return 'null'; From edeb4b418908d726d9503259f283dff9227b9b75 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 6 Nov 2023 14:36:01 +0100 Subject: [PATCH 09/12] Removed hotfix suffix from the 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 97940db30e..c4bc3b26fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.18+hotfix.2", + "version": "6.0.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.18+hotfix.2", + "version": "6.0.18", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/package.json b/package.json index 0bd14c103a..11eec6e27d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail_node", - "version": "6.0.18+hotfix.2", + "version": "6.0.18", "description": "OTNode V6", "main": "index.js", "type": "module", From b80eb28d2d84a3faffdabcbb24db66d61d823c0e Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 6 Nov 2023 15:03:37 +0100 Subject: [PATCH 10/12] Updated provider debugging --- .../blockchain/implementation/web3-service.js | 83 +++++++------------ 1 file changed, 29 insertions(+), 54 deletions(-) diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index d72c91c112..ce051c11cd 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -186,67 +186,42 @@ class Web3Service { initializeProviderDebugging() { this.provider.on('debug', (info) => { - switch (info.request.method) { - case 'call': - if (info.backend.error) { - const contractInstance = this.contractAddresses[info.request.params.to]; - const inputData = info.request.params.data; - const decodedInputData = this._decodeInputData( - inputData, - contractInstance.interface, - ); - const decodedErrorData = this._decodeErrorData( - info.backend.error, - contractInstance.interface, - ); - - this.logger.debug( - `${decodedInputData} call has failed; ` + - `Error: ${decodedErrorData}; ` + - `RPC: ${info.backend.provider.connection.url}.`, - ); - } - break; - case 'estimateGas': { - const contractInstance = this.contractAddresses[info.request.params.to]; - const inputData = info.request.params.data; - let decodedInputData; - if (info.backend.result || info.backend.result === null) { - decodedInputData = this._decodeInputData( - inputData, - contractInstance.interface, - ); + const contractInstance = this.contractAddresses[info.request.params.to]; + const inputData = info.request.params.data; + const decodedInputData = this._decodeInputData(inputData, contractInstance.interface); + + if (info.request.method === 'call' && info.backend.error) { + const decodedErrorData = this._decodeErrorData( + info.backend.error, + contractInstance.interface, + ); + this.logger.debug( + `${decodedInputData} call has failed; Error: ${decodedErrorData}; ` + + `RPC: ${info.backend.provider.connection.url}.`, + ); + } else if (info.request.method === 'estimateGas') { + if (info.backend.error) { + const decodedErrorData = this._decodeErrorData( + info.backend.error, + contractInstance.interface, + ); + this.logger.debug( + `${decodedInputData} gas estimation has failed; Error: ${decodedErrorData}; ` + + `RPC: ${info.backend.provider.connection.url}.`, + ); + } else if (info.backend.result !== undefined) { + let message = `${decodedInputData} gas has been successfully estimated; `; + if (info.backend.result !== null) { const decodedResultData = this._decodeResultData( inputData.slice(0, 10), info.backend.result, contractInstance.interface, ); - - this.logger.debug( - `${decodedInputData} transaction has been successfully executed; ` + - `Result: ${decodedResultData} ` + - `RPC: ${info.backend.provider.connection.url}.`, - ); - } else if (info.backend.error) { - decodedInputData = this._decodeInputData( - inputData, - contractInstance.interface, - ); - const decodedErrorData = this._decodeErrorData( - info.backend.error, - contractInstance.interface, - ); - - this.logger.debug( - `${decodedInputData} transaction has failed; ` + - `Error: ${decodedErrorData}; ` + - `RPC: ${info.backend.provider.connection.url}.`, - ); + message += `Result: ${decodedResultData} `; } - break; + message += `RPC: ${info.backend.provider.connection.url}.`; + this.logger.debug(message); } - default: - break; } }); } From 1ee2d09594a0ec57de8a670f543e671a7c6e9bbb Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 6 Nov 2023 15:13:39 +0100 Subject: [PATCH 11/12] Only try to get contract instance for call/estimateGas inside provider debugger --- .../blockchain/implementation/web3-service.js | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index ce051c11cd..35e66880ba 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -186,31 +186,28 @@ class Web3Service { initializeProviderDebugging() { this.provider.on('debug', (info) => { - const contractInstance = this.contractAddresses[info.request.params.to]; - const inputData = info.request.params.data; - const decodedInputData = this._decodeInputData(inputData, contractInstance.interface); + const { method } = info.request; - if (info.request.method === 'call' && info.backend.error) { - const decodedErrorData = this._decodeErrorData( - info.backend.error, + if (['call', 'estimateGas'].includes(method)) { + const contractInstance = this.contractAddresses[info.request.params.to]; + const inputData = info.request.params.data; + const decodedInputData = this._decodeInputData( + inputData, contractInstance.interface, ); - this.logger.debug( - `${decodedInputData} call has failed; Error: ${decodedErrorData}; ` + - `RPC: ${info.backend.provider.connection.url}.`, - ); - } else if (info.request.method === 'estimateGas') { + if (info.backend.error) { const decodedErrorData = this._decodeErrorData( info.backend.error, contractInstance.interface, ); this.logger.debug( - `${decodedInputData} gas estimation has failed; Error: ${decodedErrorData}; ` + + `${decodedInputData} ${method} has failed; Error: ${decodedErrorData}; ` + `RPC: ${info.backend.provider.connection.url}.`, ); } else if (info.backend.result !== undefined) { - let message = `${decodedInputData} gas has been successfully estimated; `; + let message = `${decodedInputData} ${method} has been successfully executed; `; + if (info.backend.result !== null) { const decodedResultData = this._decodeResultData( inputData.slice(0, 10), @@ -219,7 +216,9 @@ class Web3Service { ); message += `Result: ${decodedResultData} `; } + message += `RPC: ${info.backend.provider.connection.url}.`; + this.logger.debug(message); } } From 702aa5486a4fbb02917f0c64daba9a496334a09a Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 6 Nov 2023 15:36:04 +0100 Subject: [PATCH 12/12] Turned off provider debugging --- src/modules/blockchain/implementation/web3-service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index 35e66880ba..63545eedf2 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -61,7 +61,7 @@ class Web3Service { await this.initializeWeb3(); this.startBlock = await this.getBlockNumber(); await this.initializeContracts(); - this.initializeProviderDebugging(); + // this.initializeProviderDebugging(); } initializeTransactionQueue(concurrency) {