diff --git a/client/protocol_versions.json b/client/protocol_versions.json index b0df841a8..77eb4c4f2 100644 --- a/client/protocol_versions.json +++ b/client/protocol_versions.json @@ -152,5 +152,8 @@ }, "1.4.1": { "min": "1.0.0" + }, + "1.4.2": { + "min": "1.0.0" } } \ No newline at end of file diff --git a/common/result-code.js b/common/result-code.js index a7ee44e92..e24646f6a 100644 --- a/common/result-code.js +++ b/common/result-code.js @@ -217,15 +217,15 @@ const EventHandlerErrorCode = { MISSING_CONFIG_IN_MSG_DATA: 70007, DUPLICATED_GLOBAL_FILTER_ID: 70008, INVALID_EVENT_TYPE_IN_VALIDATE_FUNC: 70009, - NO_MATCHED_FILTERS: 70010, - MISSING_FILTER_ID_IN_TYPE_TO_FILTER_IDS: 70011, - PARSING_GLOBAL_FILTER_ID_FAILURE: 70012, + NO_MATCHED_FILTERS: 70010, // Deprecated (2024-08-14). + MISSING_FILTER_ID_IN_TYPE_TO_FILTER_IDS: 70011, // Deprecated (2024-08-14) + PARSING_GLOBAL_FILTER_ID_FAILURE: 70012, // Deprecated (2024-08-14). DUPLICATED_CHANNEL_ID: 70013, EVENT_CHANNEL_EXCEEDS_SIZE_LIMIT: 70014, EVENT_FILTER_EXCEEDS_SIZE_LIMIT: 70015, EVENT_FILTER_EXCEEDS_SIZE_LIMIT_PER_CHANNEL: 70016, FAILED_TO_REGISTER_FILTER: 70020, - FAILED_TO_DEREGISTER_FILTER: 70030, + FAILED_TO_DEREGISTER_FILTER: 70030, // Deprecated (2024-08-14). INVALID_CUSTOM_CLIENT_ID: 70040, // BLOCK_FINALIZED (701XX) NEGATIVE_BLOCK_NUMBER: 70100, @@ -234,9 +234,9 @@ const EventHandlerErrorCode = { MISSING_PATH_IN_CONFIG: 70200, INVALID_FORMAT_PATH: 70201, // VALUE_CHANGED & StateEventTreeManager (7025X) - MISSING_FILTER_ID_IN_FILTER_ID_TO_PARSED_PATH: 70250, - MISSING_FILTER_ID_SET: 70251, - MISSING_FILTER_ID_IN_FILTER_ID_SET: 70252, + MISSING_FILTER_ID_IN_FILTER_ID_TO_PARSED_PATH: 70250, // Deprecated (2024-08-14). + MISSING_FILTER_ID_SET: 70251, // Deprecated (2024-08-14). + MISSING_FILTER_ID_IN_FILTER_ID_SET: 70252, // Deprecated (2024-08-14). // TX_STATE_CHANGED (703XX) MISSING_TX_HASH_IN_CONFIG: 70300, INVALID_TX_HASH: 70301, diff --git a/event-handler/event-channel-manager.js b/event-handler/event-channel-manager.js index cd5487f13..a7c470182 100644 --- a/event-handler/event-channel-manager.js +++ b/event-handler/event-channel-manager.js @@ -13,6 +13,8 @@ const EventHandlerError = require('./event-handler-error'); const { EventHandlerErrorCode } = require('../common/result-code'); const BlockchainEvent = require('./blockchain-event'); +const CHANNEL_ID_RANDOM_NUMBER_RANGE = 1000; + class EventChannelManager { constructor(node) { this.node = node; @@ -107,7 +109,9 @@ class EventChannelManager { `The number of event channels exceeds its limit ` + `(${NodeConfigs.MAX_NUM_EVENT_CHANNELS})`); } - const channelId = Date.now(); // NOTE: Only used in blockchain + // NOTE: Only used in blockchain + const channelId + = String(Date.now() + Math.floor(Math.random() * CHANNEL_ID_RANDOM_NUMBER_RANGE)); if (this.channels[channelId]) { // TODO(cshcomcom): Retry logic. webSocket.terminate(); throw new EventHandlerError(EventHandlerErrorCode.DUPLICATED_CHANNEL_ID, @@ -236,25 +240,15 @@ class EventChannelManager { deregisterFilter(channel, clientFilterId) { const filter = this.node.eh.deregisterEventFilter(clientFilterId, channel.id); + if (!filter) { + return; + } channel.deleteEventFilterId(filter.id); delete this.filterIdToChannelId[filter.id]; } deregisterFilterAndEmitEvent(channel, clientFilterId, filterDeletionReason) { - const LOG_HEADER = 'deregisterFilterAndEmitEvent'; - try { - this.deregisterFilter(channel, clientFilterId); - } catch (err) { - logger.error(`[${LOG_HEADER}] Can't deregister event filter ` + - `(clientFilterId: ${clientFilterId}, channelId: ${channel.id}, ` + - `err: ${err.message} at ${err.stack})`); - throw new EventHandlerError( - EventHandlerErrorCode.FAILED_TO_DEREGISTER_FILTER, - `Failed to deregister filter with filter ID: ${clientFilterId} ` + - `due to error: ${err.message}`, - clientFilterId - ); - } + this.deregisterFilter(channel, clientFilterId); const blockchainEvent = new BlockchainEvent( BlockchainEventTypes.FILTER_DELETED, { @@ -344,6 +338,9 @@ class EventChannelManager { // TODO(ehgmsdk20): reuse same object for memory const eventObj = event.toObject(); const clientFilterId = this.node.eh.getClientFilterIdFromGlobalFilterId(eventFilterId); + if (!clientFilterId) { + return; + } Object.assign(eventObj, { filter_id: clientFilterId }); this.transmitEventObj(channel, eventObj); } @@ -369,20 +366,18 @@ class EventChannelManager { closeChannel(channel) { const LOG_HEADER = 'closeChannel'; - try { - logger.info(`[${LOG_HEADER}] Closing channel ${channel.id}`); - channel.webSocket.terminate(); - const filterIds = channel.getAllFilterIds(); - for (const filterId of filterIds) { - const clientFilterId = this.node.eh.getClientFilterIdFromGlobalFilterId(filterId); - // NOTE(ehgmsdk20): Do not emit filter_deleted event because the channel is already closed. - this.deregisterFilter(channel, clientFilterId); + logger.info(`[${LOG_HEADER}] Closing channel ${channel.id}`); + channel.webSocket.terminate(); + const filterIds = channel.getAllFilterIds(); + for (const filterId of filterIds) { + const clientFilterId = this.node.eh.getClientFilterIdFromGlobalFilterId(filterId); + if (!clientFilterId) { + continue; } - delete this.channels[channel.id]; - } catch (err) { - logger.error(`[${LOG_HEADER}] Error while closing channel (channelId: ${channel.id}, ` + - `message:${err.message})`); + // NOTE(ehgmsdk20): Do not emit filter_deleted event because the channel is already closed. + this.deregisterFilter(channel, clientFilterId); } + delete this.channels[channel.id]; } startHeartbeat(wsServer) { diff --git a/event-handler/index.js b/event-handler/index.js index d4f166a76..1f4d8d1a3 100644 --- a/event-handler/index.js +++ b/event-handler/index.js @@ -191,6 +191,9 @@ class EventHandler { if (isEndState(afterState)) { const channel = this.eventChannelManager.getChannelByEventFilterId(eventFilterId); const clientFilterId = this.getClientFilterIdFromGlobalFilterId(eventFilterId); + if (!clientFilterId) { + continue; + } this.eventChannelManager.deregisterFilterAndEmitEvent( channel, clientFilterId, FilterDeletionReasons.END_STATE_REACHED ); @@ -213,6 +216,9 @@ class EventHandler { this.eventFilterIdToTimeoutCallback.set(eventFilterId, setTimeout(() => { const channel = this.eventChannelManager.getChannelByEventFilterId(eventFilterId); const clientFilterId = this.getClientFilterIdFromGlobalFilterId(eventFilterId); + if (!clientFilterId) { + return; + } this.eventChannelManager.deregisterFilterAndEmitEvent( channel, clientFilterId, FilterDeletionReasons.FILTER_TIMEOUT ); @@ -220,10 +226,11 @@ class EventHandler { } getClientFilterIdFromGlobalFilterId(globalFilterId) { + const LOG_HEADER = 'getClientFilterIdFromGlobalFilterId'; const clientFilterId = globalFilterId.split(':')[1]; if (!clientFilterId) { - throw new EventHandlerError(EventHandlerErrorCode.PARSING_GLOBAL_FILTER_ID_FAILURE, - `Can't get client filter ID from global filter ID (globalFilterId: ${globalFilterId})`); + logger.error(`[${LOG_HEADER}] Can't get client filter ID from global filter ID (globalFilterId: ${globalFilterId})`); + return null; } return clientFilterId; } @@ -304,13 +311,13 @@ class EventHandler { const eventFilterId = this.getGlobalFilterId(channelId, clientFilterId); const eventFilter = this.eventFilters[eventFilterId]; if (!eventFilter) { - throw new EventHandlerError(EventHandlerErrorCode.NO_MATCHED_FILTERS, - `Can't find filter by filter id`, eventFilterId); + logger.error(`[${LOG_HEADER}] Can't find filter by filter id (eventFilterId: ${eventFilterId})`); + return null; } delete this.eventFilters[eventFilterId]; if (!this.eventTypeToEventFilterIds[eventFilter.type].delete(eventFilterId)) { - throw new EventHandlerError(EventHandlerErrorCode.MISSING_FILTER_ID_IN_TYPE_TO_FILTER_IDS, - `Can't delete filter Id from eventTypeToEventFilterIds (${eventFilterId})`); + logger.error(`[${LOG_HEADER}] Can't delete filter Id from eventTypeToEventFilterIds (eventFilterId: ${eventFilterId})`); + return null; } if (eventFilter.type === BlockchainEventTypes.VALUE_CHANGED) { this.stateEventTreeManager.deregisterEventFilterId(eventFilterId); diff --git a/event-handler/state-event-tree-manager.js b/event-handler/state-event-tree-manager.js index 56db0db90..666f853b0 100644 --- a/event-handler/state-event-tree-manager.js +++ b/event-handler/state-event-tree-manager.js @@ -87,24 +87,24 @@ class StateEventTreeManager { } deleteFilterIdFromEventNode(eventNode, filterId) { + const LOG_HEADER = 'deleteFilterIdFromEventNode'; if (!eventNode || !eventNode.filterIdSet) { - throw new EventHandlerError(EventHandlerErrorCode.MISSING_FILTER_ID_SET, - `Can't find filterIdSet (eventNode: ${JSON.stringify(eventNode)})`); + logger.error(`[${LOG_HEADER}] Can't find filterIdSet (eventNode: ${JSON.stringify(eventNode)})`); + return; } if (!eventNode.filterIdSet.delete(filterId)) { - throw new EventHandlerError(EventHandlerErrorCode.MISSING_FILTER_ID_IN_FILTER_ID_SET, - `Can't delete filter id (${filterId}) from filterIdSet ` + + logger.error(`[${LOG_HEADER}] Can't delete filter id (${filterId}) from filterIdSet ` + `(${JSON.stringify(eventNode.filterIdSet.values())})`); + return; } } deregisterEventFilterId(filterId) { + const LOG_HEADER = 'deregisterEventFilterId'; const parsedPath = this.filterIdToParsedPath[filterId]; if (!parsedPath) { - throw new EventHandlerError( - EventHandlerErrorCode.MISSING_FILTER_ID_IN_FILTER_ID_TO_PARSED_PATH, - `Can't find parsedPath from filterIdToParsedPath (${filterId})` - ); + logger.error(`[${LOG_HEADER}] Can't find parsedPath from filterIdToParsedPath (filterId: ${filterId})`); + return; } delete this.filterIdToParsedPath[filterId]; diff --git a/package.json b/package.json index 5074b4fdb..0aad3df76 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ain-blockchain", "description": "AI Network Blockchain", - "version": "1.4.1", + "version": "1.4.2", "private": true, "license": "MIT", "author": "dev@ainetwork.ai", diff --git a/test/integration/event_handler.test.js b/test/integration/event_handler.test.js index c0fc19b7f..db4f1fb1a 100644 --- a/test/integration/event_handler.test.js +++ b/test/integration/event_handler.test.js @@ -254,26 +254,6 @@ describe('Event Handler Test', function() { deregisterFilter(wsClient, filterId); }); - it('Deregister filter already deregisterd filter', function(done) { - // Deregister filter - wsClient.on('message', (message) => { - try { - const parsedMessage = JSON.parse(message); - const messageType = parsedMessage.type; - const errorCode = _.get(parsedMessage, 'data.code'); - const errorMessage = _.get(parsedMessage, 'data.message'); - if (messageType === BlockchainEventMessageTypes.EMIT_ERROR) { - expect(errorCode).to.equal(EventHandlerErrorCode.FAILED_TO_DEREGISTER_FILTER); - expect(errorMessage).to.equal(`Failed to deregister filter with filter ID: ${filterId} due to error: Can't find filter by filter id`) - done(); - } - } catch (err) { - done(err); - } - }); - deregisterFilter(wsClient, filterId); - }); - it('Register too many filters', function(done) { let exceededCnt = 0; wsClient.on('message', (message) => {