From a814748dfb39dc70302becbca95c19606e3bab8e Mon Sep 17 00:00:00 2001 From: vincentwschau <99756290+vincentwschau@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:35:37 -0400 Subject: [PATCH] [IND-445] Add legal copy for websocket error messages for blocked addresses. (#622) --- .../socks/__tests__/lib/subscriptions.test.ts | 35 ++++++++++++++++++- indexer/services/socks/src/lib/errors.ts | 9 +++++ .../services/socks/src/lib/subscription.ts | 16 +++++++-- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/indexer/services/socks/__tests__/lib/subscriptions.test.ts b/indexer/services/socks/__tests__/lib/subscriptions.test.ts index 5a9d677d64..8cd3711ff1 100644 --- a/indexer/services/socks/__tests__/lib/subscriptions.test.ts +++ b/indexer/services/socks/__tests__/lib/subscriptions.test.ts @@ -8,7 +8,8 @@ import { } from '@dydxprotocol-indexer/postgres'; import { btcTicker, invalidChannel, invalidTicker } from '../constants'; import { axiosRequest } from '../../src/lib/axios'; -import { makeAxiosSafeServerError } from '@dydxprotocol-indexer/base'; +import { AxiosSafeServerError, makeAxiosSafeServerError } from '@dydxprotocol-indexer/base'; +import { BlockedError } from '../../src/lib/errors'; jest.mock('ws'); jest.mock('../../src/helpers/wss'); @@ -217,6 +218,38 @@ describe('Subscriptions', () => { expect(subscriptions.subscriptionLists[connectionId]).toBeUndefined(); }); + it('sends blocked error message if initial message request fails with 403', async () => { + const expectedError: BlockedError = new BlockedError(); + axiosRequestMock.mockImplementation( + () => { + throw new AxiosSafeServerError({ + data: {}, + status: 403, + statusText: '', + }, {}); + }, + ); + await subscriptions.subscribe( + mockWs, + Channel.V4_ACCOUNTS, + connectionId, + initialMsgId, + mockSubaccountId, + ); + + expect(sendMessageMock).toHaveBeenCalledTimes(1); + expect(sendMessageMock).toHaveBeenCalledWith( + mockWs, + connectionId, + expect.objectContaining({ + connection_id: connectionId, + type: 'error', + message: expectedError.message, + })); + expect(subscriptions.subscriptions[Channel.V4_ACCOUNTS]).toBeUndefined(); + expect(subscriptions.subscriptionLists[connectionId]).toBeUndefined(); + }); + it('sends empty contents if initial message request fails with 404 for accounts', async () => { axiosRequestMock.mockImplementation(() => { return Promise.reject(makeAxiosSafeServerError(404, '', '')); diff --git a/indexer/services/socks/src/lib/errors.ts b/indexer/services/socks/src/lib/errors.ts index 58769b19ee..7501de2723 100644 --- a/indexer/services/socks/src/lib/errors.ts +++ b/indexer/services/socks/src/lib/errors.ts @@ -1,3 +1,5 @@ +import { INDEXER_COMPLIANCE_BLOCKED_PAYLOAD } from '@dydxprotocol-indexer/compliance'; + export class InvalidForwardMessageError extends Error { constructor(message: string) { super(`Invalid forwarded message. Error: ${message}.`); @@ -18,3 +20,10 @@ export class InvalidTopicError extends Error { this.name = 'InvalidTopicError'; } } + +export class BlockedError extends Error { + constructor() { + super(INDEXER_COMPLIANCE_BLOCKED_PAYLOAD); + this.name = 'BlockedError'; + } +} diff --git a/indexer/services/socks/src/lib/subscription.ts b/indexer/services/socks/src/lib/subscription.ts index 1da7eeea35..71a8a02ecb 100644 --- a/indexer/services/socks/src/lib/subscription.ts +++ b/indexer/services/socks/src/lib/subscription.ts @@ -18,7 +18,7 @@ import { } from '../types'; import { axiosRequest } from './axios'; import { V4_MARKETS_ID, WS_CLOSE_CODE_POLICY_VIOLATION } from './constants'; -import { InvalidChannelError } from './errors'; +import { BlockedError, InvalidChannelError } from './errors'; import { RateLimiter } from './rate-limit'; const COMLINK_URL: string = `http://${config.COMLINK_URL}`; @@ -140,11 +140,18 @@ export class Subscriptions { id, }); + // For blocked errors add the erorr message into the error message sent in the websocket + // connection + let errorMsg: string = `Internal error, could not fetch data for subscription: ${channel}.`; + if (error instanceof BlockedError) { + errorMsg = error.message; + } + sendMessage( ws, connectionId, createErrorMessage( - `Internal error, could not fetch data for subscription: ${channel}`, + errorMsg, connectionId, messageId, ), @@ -521,6 +528,11 @@ export class Subscriptions { if (error instanceof AxiosSafeServerError && (error as AxiosSafeServerError).status === 404) { return EMPTY_INITIAL_RESPONSE; } + // 403 indicates a blocked address. Throw a specific error for blocked addresses with a + // specific error message detailing why the subscription failed due to a blocked address. + if (error instanceof AxiosSafeServerError && (error as AxiosSafeServerError).status === 403) { + throw new BlockedError(); + } throw error; } }