From 65a45b943956cf011fb9e0d63eea646e314807af Mon Sep 17 00:00:00 2001 From: ebadiere Date: Wed, 28 Feb 2024 18:44:08 -0700 Subject: [PATCH 1/5] feat: adding polling for new heads. Signed-off-by: ebadiere --- package.json | 3 +- packages/relay/src/lib/poller.ts | 6 +- .../tests/acceptance/ws/subscribe.spec.ts | 21 -- .../acceptance/ws/subscribeNewHeads.spec.ts | 99 +++++++++ packages/server/tests/localAcceptance.env | 1 + .../server/tests/previewnetAcceptance.env | 2 + packages/ws-server/src/webSocketServer.ts | 189 ++++++++++++------ 7 files changed, 234 insertions(+), 87 deletions(-) create mode 100644 packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts diff --git a/package.json b/package.json index 980661b7ca..c3fbb260a9 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "acceptancetest:htsprecompilev1": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@htsprecompilev1' --exit", "acceptancetest:release": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@release' --exit", "acceptancetest:ws": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@web-socket' --exit", + "acceptancetest:ws_newheads": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@web-socket-newheads' --exit", "acceptancetest:precompile-calls": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@precompile-calls' --exit", "acceptancetest:cache-service": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@cache-service' --exit", "acceptancetest:rpc_api_schema_conformity": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@api-conformity' --exit", @@ -72,7 +73,7 @@ "keyv-file": "^0.3.0", "koa-cors": "^0.0.16", "koa-websocket": "^7.0.0", - "lerna": "^8.1.1", + "lerna": "^8.0.2", "pino": "^7.11.0", "pino-pretty": "^7.6.1", "prom-client": "^14.0.1", diff --git a/packages/relay/src/lib/poller.ts b/packages/relay/src/lib/poller.ts index 66be4babc6..aed66446b6 100644 --- a/packages/relay/src/lib/poller.ts +++ b/packages/relay/src/lib/poller.ts @@ -2,7 +2,7 @@ * * Hedera JSON RPC Relay * - * Copyright (C) 2023 Hedera Hashgraph, LLC + * Copyright (C) 2023-2024 Hedera Hashgraph, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,6 +71,10 @@ export class Poller { filters?.topics || null, ); + poll.lastPolled = this.latestBlock; + } else if (event === 'newHeads' && process.env.WS_NEW_HEADS_ENABLED === 'true') { + data = await this.eth.getBlockByNumber('latest', true); + data.jsonrpc = '2.0'; poll.lastPolled = this.latestBlock; } else { this.logger.error(`${LOGGER_PREFIX} Polling for unsupported event: ${event}. Tag: ${poll.tag}`); diff --git a/packages/server/tests/acceptance/ws/subscribe.spec.ts b/packages/server/tests/acceptance/ws/subscribe.spec.ts index 34b4f87f60..20dcad13bd 100644 --- a/packages/server/tests/acceptance/ws/subscribe.spec.ts +++ b/packages/server/tests/acceptance/ws/subscribe.spec.ts @@ -356,27 +356,6 @@ describe('@web-socket Acceptance Tests', async function () { await new Promise((resolve) => setTimeout(resolve, 500)); }); - it('Expect Unsupported Method Error message when subscribing for newHeads method', async function () { - const webSocket = new WebSocket(WS_RELAY_URL); - let response = {}; - webSocket.on('message', function incoming(data) { - response = JSON.parse(data); - }); - webSocket.on('open', function open() { - webSocket.send('{"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":1}'); - }); - - // wait 500ms to expect the message - await new Promise((resolve) => setTimeout(resolve, 500)); - - expect(response.error.code).to.eq(predefined.UNSUPPORTED_METHOD.code); - expect(response.error.name).to.eq(predefined.UNSUPPORTED_METHOD.name); - expect(response.error.message).to.eq(predefined.UNSUPPORTED_METHOD.message); - - // close the connection - webSocket.close(); - }); - it('Expect Unsupported Method Error message when subscribing for newPendingTransactions method', async function () { const webSocket = new WebSocket(WS_RELAY_URL); let response = {}; diff --git a/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts b/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts new file mode 100644 index 0000000000..2a9684346c --- /dev/null +++ b/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts @@ -0,0 +1,99 @@ +/*- + * + * Hedera JSON RPC Relay + * + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// external resources +import { solidity } from 'ethereum-waffle'; +import chai, { expect } from 'chai'; +import WebSocket from 'ws'; +chai.use(solidity); + +import { ethers } from 'ethers'; +const WS_RELAY_URL = `${process.env.WS_RELAY_URL}`; + +describe('@web-socket-newheads Acceptance Tests', async function () { + this.timeout(240 * 1000); // 240 seconds + + let server; + + let wsProvider; + let originalWsNewHeadsEnabledValue; + + before(async () => { + const { socketServer } = global; + server = socketServer; + + // cache original ENV values + originalWsNewHeadsEnabledValue = process.env.WS_NEW_HEADS_ENABLED; + }); + + beforeEach(async () => { + // restore original ENV value + process.env.WS_NEW_HEADS_ENABLED = originalWsNewHeadsEnabledValue; + + wsProvider = await new ethers.WebSocketProvider(WS_RELAY_URL); + await new Promise((resolve) => setTimeout(resolve, 1000)); + if (server) expect(server._connections).to.equal(1); + }); + + afterEach(async () => { + if (wsProvider) { + await wsProvider.destroy(); + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + if (server) expect(server._connections).to.equal(0); + }); + + describe('Configuration', async function () { + it('When WS_NEW_HEADS_ENABLED is set to false ', async function () { + const webSocket = new WebSocket(WS_RELAY_URL); + process.env.WS_NEW_HEADS_ENABLED = 'false'; + let response = ''; + const messagePromise = new Promise((resolve, reject) => { + webSocket.on('message', function incoming(data) { + try { + const response = JSON.parse(data); + expect(response).to.have.property('error'); + expect(response.error).to.have.property('code'); + expect(response.error.code).to.equal(-32601); + expect(response.error).to.have.property('message'); + expect(response.error.message).to.equal('Unsupported JSON-RPC method'); + expect(response.error).to.have.property('name'); + expect(response.error.name).to.equal('Method not found'); + resolve(); + } catch (error) { + reject(error); + } + response = data; + }); + webSocket.on('open', function open() { + // send the request for newHeads + webSocket.send('{"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]}'); + }); + webSocket.on('error', (error) => { + reject(error); // Reject the promise on WebSocket error + }); + }); + await messagePromise; + + webSocket.close(); + process.env.WS_NEW_HEADS_ENABLED = originalWsNewHeadsEnabledValue; + }); + }); +}); diff --git a/packages/server/tests/localAcceptance.env b/packages/server/tests/localAcceptance.env index 991b6671bc..1a5c43009e 100644 --- a/packages/server/tests/localAcceptance.env +++ b/packages/server/tests/localAcceptance.env @@ -20,3 +20,4 @@ DEBUG_API_ENABLED=true SEND_RAW_TRANSACTION_SIZE_LIMIT=131072 BATCH_REQUESTS_ENABLED=true TEST_GAS_PRICE_DEVIATION=0.2 +WS_NEW_HEADS_ENABLED=true diff --git a/packages/server/tests/previewnetAcceptance.env b/packages/server/tests/previewnetAcceptance.env index 076772d63e..f58f3db13d 100644 --- a/packages/server/tests/previewnetAcceptance.env +++ b/packages/server/tests/previewnetAcceptance.env @@ -15,3 +15,5 @@ SUBSCRIPTIONS_ENABLED=true FILTER_API_ENABLED=true DEBUG_API_ENABLED=true TEST_GAS_PRICE_DEVIATION=0.2 +WS_NEW_HEADS_ENABLED=true + diff --git a/packages/ws-server/src/webSocketServer.ts b/packages/ws-server/src/webSocketServer.ts index b0e1c3f86c..6405f78f5b 100644 --- a/packages/ws-server/src/webSocketServer.ts +++ b/packages/ws-server/src/webSocketServer.ts @@ -1,8 +1,8 @@ -/*- +/* - * * Hedera JSON RPC Relay * - * Copyright (C) 2023 Hedera Hashgraph, LLC + * Copyright (C) 2023-2024 Hedera Hashgraph, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,13 +19,12 @@ */ import dotenv from 'dotenv'; import path from 'path'; -dotenv.config({ path: path.resolve(__dirname, '../../../.env') }); import Koa from 'koa'; import jsonResp from '@hashgraph/json-rpc-server/dist/koaJsonRpc/lib/RpcResponse'; import KoaJsonRpc from '@hashgraph/json-rpc-server/dist/koaJsonRpc'; import websockify from 'koa-websocket'; -import { Relay, RelayImpl, predefined, JsonRpcError } from '@hashgraph/json-rpc-relay'; +import { type Relay, RelayImpl, predefined, JsonRpcError } from '@hashgraph/json-rpc-relay'; import { Registry, Counter } from 'prom-client'; import pino from 'pino'; @@ -34,6 +33,8 @@ import { formatRequestIdMessage } from '@hashgraph/json-rpc-relay/dist/formatter import { EthSubscribeLogsParamsObject } from '@hashgraph/json-rpc-server/dist/validator'; import { v4 as uuid } from 'uuid'; import constants from '@hashgraph/json-rpc-relay/dist/lib/constants'; +import { log } from 'console'; +dotenv.config({ path: path.resolve(__dirname, '../../../.env') }); const mainLogger = pino({ name: 'hedera-json-rpc-relay', @@ -100,7 +101,7 @@ async function validateIsContractOrTokenAddress(address, requestId) { if (!isContractOrToken) { throw new JsonRpcError( predefined.INVALID_PARAMETER( - `filters.address`, + 'filters.address', `${address} is not a valid contract or token type or does not exists`, ), requestId, @@ -175,71 +176,81 @@ app.ws.use(async (ctx) => { methodsCounter.labels(method).inc(); methodsCounterByIp.labels(ctx.request.ip, method).inc(); - if (method === constants.METHODS.ETH_SUBSCRIBE) { - if (limiter.validateSubscriptionLimit(ctx)) { - const event = params[0]; - const filters = params[1]; - let subscriptionId; - - if (event === constants.SUBSCRIBE_EVENTS.LOGS) { - try { - await validateSubscribeEthLogsParams(filters, requestIdPrefix); - } catch (error) { - logger.error( - error, - `${connectionIdPrefix} ${requestIdPrefix} Encountered error on ${ - ctx.websocket.id - }, method: ${method}, params: ${JSON.stringify(params)}`, - ); - response = jsonResp(request.id, error, undefined); - ctx.websocket.send(JSON.stringify(response)); - return; - } + // Early return if subscription limit is exceeded + if (method === constants.METHODS.ETH_SUBSCRIBE && !limiter.validateSubscriptionLimit(ctx)) { + response = jsonResp(request.id, predefined.MAX_SUBSCRIPTIONS, undefined); + ctx.websocket.send(JSON.stringify(response)); + return; + } - if (!getMultipleAddressesEnabled() && Array.isArray(filters.address) && filters.address.length > 1) { - response = jsonResp( - request.id, - predefined.INVALID_PARAMETER('filters.address', 'Only one contract address is allowed'), - undefined, - ); - } else { - subscriptionId = relay.subs()?.subscribe(ctx.websocket, event, filters); + try { + switch (method) { + case constants.METHODS.ETH_SUBSCRIBE: { + const event = params[0]; + const filters = params[1]; + let subscriptionId; + + switch (event) { + case constants.SUBSCRIBE_EVENTS.LOGS: + ({ response, subscriptionId } = await handleEthSubscribeLogs( + filters, + requestIdPrefix, + response, + request, + subscriptionId, + ctx, + event, + )); + break; + + case constants.SUBSCRIBE_EVENTS.NEW_HEADS: + ({ response, subscriptionId } = handleEthSubscribeNewHeads( + response, + subscriptionId, + filters, + request, + ctx, + event, + )); + break; + case constants.SUBSCRIBE_EVENTS.NEW_PENDING_TRANSACTIONS: + response = jsonResp(request.id, predefined.UNSUPPORTED_METHOD, undefined); + break; + + default: + response = jsonResp(request.id, predefined.UNSUPPORTED_METHOD, undefined); } - } else if (event === constants.SUBSCRIBE_EVENTS.NEW_HEADS) { - response = jsonResp(request.id, predefined.UNSUPPORTED_METHOD, undefined); - } else if (event === constants.SUBSCRIBE_EVENTS.NEW_PENDING_TRANSACTIONS) { - response = jsonResp(request.id, predefined.UNSUPPORTED_METHOD, undefined); - } else { - response = jsonResp(request.id, predefined.UNSUPPORTED_METHOD, undefined); - } - limiter.incrementSubs(ctx); - - if (subscriptionId) { - response = jsonResp(request.id, null, subscriptionId); + limiter.incrementSubs(ctx); + response = response ?? (subscriptionId ? jsonResp(request.id, null, subscriptionId) : undefined); + break; } - } else { - response = jsonResp(request.id, predefined.MAX_SUBSCRIPTIONS, undefined); - } - } else if (method === constants.METHODS.ETH_UNSUBSCRIBE) { - const subId = params[0]; - const unsubbedCount = relay.subs()?.unsubscribe(ctx.websocket, subId); - const success = unsubbedCount !== 0; - if (success) { - limiter.decrementSubs(ctx, unsubbedCount); + case constants.METHODS.ETH_UNSUBSCRIBE: { + const subId = params[0]; + const unsubbedCount = relay.subs()?.unsubscribe(ctx.websocket, subId); + limiter.decrementSubs(ctx, unsubbedCount); + response = jsonResp(request.id, null, unsubbedCount !== 0); + break; + } + case constants.METHODS.ETH_CHAIN_ID: + response = jsonResp(request.id, null, CHAIN_ID); + break; + default: + response = jsonResp(request.id, DEFAULT_ERROR, null); } - - response = jsonResp(request.id, null, success); + } catch (error) { + logger.error( + error, + `${connectionIdPrefix} ${requestIdPrefix} Encountered error on ${ + ctx.websocket.id + }, method: ${method}, params: ${JSON.stringify(params)}`, + ); + response = jsonResp(request.id, error, undefined); } - // Clients want to know the chainId after connecting - else if (method === constants.METHODS.ETH_CHAIN_ID) { - response = jsonResp(request.id, null, CHAIN_ID); - } else { - response = jsonResp(request.id, DEFAULT_ERROR, null); + if (response) { + ctx.websocket.send(JSON.stringify(response)); } - - ctx.websocket.send(JSON.stringify(response)); }); if (pingInterval > 0) { @@ -269,7 +280,7 @@ httpApp.use(async (ctx, next) => { */ try { const result = relay.eth().chainId(); - if (result.indexOf('0x12') >= 0) { + if (result.includes('0x12')) { ctx.status = 200; ctx.body = 'OK'; } else { @@ -281,7 +292,7 @@ httpApp.use(async (ctx, next) => { throw e; } } else { - return next(); + return await next(); } }); @@ -298,3 +309,53 @@ process.on('uncaughtException', (err) => { }); export { app, httpApp }; +function handleEthSubscribeNewHeads( + response: any, + subscriptionId: any, + filters: any, + request: any, + ctx: any, + event: any, +) { + if (process.env.WS_NEW_HEADS_ENABLED === 'true') { + ({ response, subscriptionId } = subscribeToNewHeads(filters, response, request, subscriptionId, ctx, event)); + } else { + response = jsonResp(request.id, predefined.UNSUPPORTED_METHOD, undefined); + } + return { response, subscriptionId }; +} + +async function handleEthSubscribeLogs( + filters: any, + requestIdPrefix: string, + response: any, + request: any, + subscriptionId: any, + ctx: any, + event: any, +) { + await validateSubscribeEthLogsParams(filters, requestIdPrefix); + if (!getMultipleAddressesEnabled() && Array.isArray(filters.address) && filters.address.length > 1) { + response = jsonResp( + request.id, + predefined.INVALID_PARAMETER('filters.address', 'Only one contract address is allowed'), + undefined, + ); + } else { + subscriptionId = relay.subs()?.subscribe(ctx.websocket, event, filters); + } + return { response, subscriptionId }; +} + +function subscribeToNewHeads(filters: any, response: any, request: any, subscriptionId: any, ctx: any, event: any) { + if (filters !== undefined) { + response = jsonResp( + request.id, + predefined.INVALID_PARAMETER('filters', 'Filters should be undefined for newHeads event'), + undefined, + ); + } + subscriptionId = relay.subs()?.subscribe(ctx.websocket, event, 'newHeads'); + logger.info(`Subscribed to newHeads, subscriptionId: ${subscriptionId}`); + return { response, subscriptionId }; +} From 326b69a2396616a53d5c60ace963f40f93385254 Mon Sep 17 00:00:00 2001 From: ebadiere Date: Thu, 29 Feb 2024 14:34:58 -0700 Subject: [PATCH 2/5] fix: Test fix. Isolated the WS_SUBSCRIPTION_LIMIT setting for the test. Signed-off-by: ebadiere --- .../tests/acceptance/ws/subscribeNewHeads.spec.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts b/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts index 2a9684346c..cbd147c658 100644 --- a/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts +++ b/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts @@ -27,13 +27,13 @@ chai.use(solidity); import { ethers } from 'ethers'; const WS_RELAY_URL = `${process.env.WS_RELAY_URL}`; -describe('@web-socket-newheads Acceptance Tests', async function () { +describe('@web-socket Acceptance Tests', async function () { this.timeout(240 * 1000); // 240 seconds let server; let wsProvider; - let originalWsNewHeadsEnabledValue; + let originalWsNewHeadsEnabledValue, originalWsSubcriptionLimitValue; before(async () => { const { socketServer } = global; @@ -41,12 +41,14 @@ describe('@web-socket-newheads Acceptance Tests', async function () { // cache original ENV values originalWsNewHeadsEnabledValue = process.env.WS_NEW_HEADS_ENABLED; + originalWsSubcriptionLimitValue = process.env.WS_SUBSCRIPTION_LIMIT; }); beforeEach(async () => { - // restore original ENV value process.env.WS_NEW_HEADS_ENABLED = originalWsNewHeadsEnabledValue; + process.env.WS_SUBSCRIPTION_LIMIT = '10'; + wsProvider = await new ethers.WebSocketProvider(WS_RELAY_URL); await new Promise((resolve) => setTimeout(resolve, 1000)); if (server) expect(server._connections).to.equal(1); @@ -58,10 +60,11 @@ describe('@web-socket-newheads Acceptance Tests', async function () { await new Promise((resolve) => setTimeout(resolve, 1000)); } if (server) expect(server._connections).to.equal(0); + process.env.WS_SUBSCRIPTION_LIMIT = originalWsSubcriptionLimitValue; }); describe('Configuration', async function () { - it('When WS_NEW_HEADS_ENABLED is set to false ', async function () { + it('When WS_NEW_HEADS_ENABLED is set to false it should return unsupported method', async function () { const webSocket = new WebSocket(WS_RELAY_URL); process.env.WS_NEW_HEADS_ENABLED = 'false'; let response = ''; From c27648847fb29581e1b2c6a38fa7e520bc8f2eb4 Mon Sep 17 00:00:00 2001 From: ebadiere Date: Fri, 1 Mar 2024 08:34:58 -0700 Subject: [PATCH 3/5] fix: Fixed lerna version and set WS_NEW_HEADS_ENABLED for localAccpentance to false. Signed-off-by: ebadiere --- package.json | 2 +- packages/server/tests/localAcceptance.env | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c3fbb260a9..a1d6cded5b 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "keyv-file": "^0.3.0", "koa-cors": "^0.0.16", "koa-websocket": "^7.0.0", - "lerna": "^8.0.2", + "lerna": "^8.1.1", "pino": "^7.11.0", "pino-pretty": "^7.6.1", "prom-client": "^14.0.1", diff --git a/packages/server/tests/localAcceptance.env b/packages/server/tests/localAcceptance.env index 1a5c43009e..a981b54647 100644 --- a/packages/server/tests/localAcceptance.env +++ b/packages/server/tests/localAcceptance.env @@ -20,4 +20,4 @@ DEBUG_API_ENABLED=true SEND_RAW_TRANSACTION_SIZE_LIMIT=131072 BATCH_REQUESTS_ENABLED=true TEST_GAS_PRICE_DEVIATION=0.2 -WS_NEW_HEADS_ENABLED=true +WS_NEW_HEADS_ENABLED=false From f404f2484eaefac258c4ff3960c3384a31d94d3d Mon Sep 17 00:00:00 2001 From: ebadiere Date: Fri, 1 Mar 2024 11:29:16 -0700 Subject: [PATCH 4/5] feat: Added test for number of subscriptions. Signed-off-by: ebadiere --- .../acceptance/ws/subscribeNewHeads.spec.ts | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts b/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts index cbd147c658..5ba27107fe 100644 --- a/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts +++ b/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts @@ -25,8 +25,40 @@ import WebSocket from 'ws'; chai.use(solidity); import { ethers } from 'ethers'; +import Assertions from '../../helpers/assertions'; +import { predefined } from '@hashgraph/json-rpc-relay'; const WS_RELAY_URL = `${process.env.WS_RELAY_URL}`; +const createSubscription = (ws, subscriptionId) => { + return new Promise((resolve, reject) => { + ws.on('message', function incoming(data) { + const response = JSON.parse(data); + if (response.id === subscriptionId && response.result) { + console.log(`Subscription ${subscriptionId} successful with ID: ${response.result}`); + resolve(response.result); // Resolve with the subscription ID + } else if (response.method === 'eth_subscription') { + console.log(`Subscription ${subscriptionId} received block:`, response.params.result); + // You can add more logic here to handle incoming blocks + } + }); + + ws.on('open', function open() { + ws.send( + JSON.stringify({ + id: subscriptionId, + jsonrpc: '2.0', + method: 'eth_subscribe', + params: ['newHeads'], + }), + ); + }); + + ws.on('error', (error) => { + reject(error); + }); + }); +}; + describe('@web-socket Acceptance Tests', async function () { this.timeout(240 * 1000); // 240 seconds @@ -64,7 +96,7 @@ describe('@web-socket Acceptance Tests', async function () { }); describe('Configuration', async function () { - it('When WS_NEW_HEADS_ENABLED is set to false it should return unsupported method', async function () { + it('Should return unsupported method when WS_NEW_HEADS_ENABLED is set to false', async function () { const webSocket = new WebSocket(WS_RELAY_URL); process.env.WS_NEW_HEADS_ENABLED = 'false'; let response = ''; @@ -98,5 +130,23 @@ describe('@web-socket Acceptance Tests', async function () { webSocket.close(); process.env.WS_NEW_HEADS_ENABLED = originalWsNewHeadsEnabledValue; }); + + it('Does not allow more subscriptions per connection than the specified limit with newHeads', async function () { + process.env.WS_SUBSCRIPTION_LIMIT = '2'; + // Create different subscriptions + for (let i = 0; i < 3; i++) { + if (i === 2) { + const expectedError = predefined.MAX_SUBSCRIPTIONS; + await Assertions.assertPredefinedRpcError(expectedError, wsProvider.send, true, wsProvider, [ + 'eth_subscribe', + ['newHeads'], + ]); + } else { + await wsProvider.send('eth_subscribe', ['newHeads']); + } + } + + await new Promise((resolve) => setTimeout(resolve, 500)); + }); }); }); From 3cd0af442cdb1d7af634f2d19e1d212eb60c9925 Mon Sep 17 00:00:00 2001 From: ebadiere Date: Tue, 5 Mar 2024 11:42:11 -0700 Subject: [PATCH 5/5] fix: Test bug fix. Signed-off-by: ebadiere --- packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts b/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts index 5ba27107fe..37df7c00a9 100644 --- a/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts +++ b/packages/server/tests/acceptance/ws/subscribeNewHeads.spec.ts @@ -133,6 +133,7 @@ describe('@web-socket Acceptance Tests', async function () { it('Does not allow more subscriptions per connection than the specified limit with newHeads', async function () { process.env.WS_SUBSCRIPTION_LIMIT = '2'; + process.env.WS_NEW_HEADS_ENABLED = 'true'; // Create different subscriptions for (let i = 0; i < 3; i++) { if (i === 2) {