From e185d9dcaaca27b737c985d330a611723736539a Mon Sep 17 00:00:00 2001 From: AlexNi245 Date: Tue, 30 Jul 2024 15:11:39 +0200 Subject: [PATCH] use receiver address in WebsocketManager --- .../src/message/MessageProcessor.ts | 2 +- .../src/ws/WebSocketManager.test.ts | 163 ++++++++++++++---- .../src/ws/WebSocketManager.ts | 36 ++-- 3 files changed, 152 insertions(+), 49 deletions(-) diff --git a/packages/delivery-service/src/message/MessageProcessor.ts b/packages/delivery-service/src/message/MessageProcessor.ts index 71e1172ac..3b53383df 100644 --- a/packages/delivery-service/src/message/MessageProcessor.ts +++ b/packages/delivery-service/src/message/MessageProcessor.ts @@ -155,7 +155,7 @@ export class MessageProcessor { } //If there is currently a webSocket connection open to the receiver, the message will be directly send. - if (await this.webSocketManager.isConnected(deliveryInformation.to)) { + if (await this.webSocketManager.isConnected(receiverAddress)) { //Client is already connect to the delivery service and the message can be dispatched //TODO MOVE send method to the WebSocketManager this.onSubmitMessage( diff --git a/packages/delivery-service/src/ws/WebSocketManager.test.ts b/packages/delivery-service/src/ws/WebSocketManager.test.ts index aceed7abf..4b53a2fcc 100644 --- a/packages/delivery-service/src/ws/WebSocketManager.test.ts +++ b/packages/delivery-service/src/ws/WebSocketManager.test.ts @@ -3,6 +3,14 @@ import { Server as SocketIoServer } from 'socket.io'; import { createServer, Server as HttpServerType } from 'http'; import { AUTHORIZED, UNAUTHORIZED, WebSocketManager } from './WebSocketManager'; import { generateAuthJWT } from '@dm3-org/dm3-lib-delivery'; +import { + getMockDeliveryServiceProfile, + MockDeliveryServiceProfile, + MockedUserProfile, + mockUserProfile, +} from '@dm3-org/dm3-lib-test-helper'; +import { ethers } from 'ethers'; +import account from '../persistence/account'; const serverSecret = 'verySecretAndImportantServerSecret'; describe('WebSocketManager', () => { @@ -12,6 +20,13 @@ describe('WebSocketManager', () => { let httpServer; let socketIoServer; + let sender: MockedUserProfile; + let receiver: MockedUserProfile; + let receiverOnGno: MockedUserProfile; + let rando: MockedUserProfile; + + let ds: MockDeliveryServiceProfile; + beforeEach(async () => { httpServer = await mockHttpServer(4060); socketIoServer = new SocketIoServer(httpServer, { @@ -22,6 +37,31 @@ describe('WebSocketManager', () => { optionsSuccessStatus: 204, }, }); + + //The receiver might use the same address for different networks. Hence we keep the wallet separate + + const receiverWallet = ethers.Wallet.createRandom(); + sender = await mockUserProfile( + ethers.Wallet.createRandom(), + 'bob.eth', + ['http://localhost:3000'], + ); + receiver = await mockUserProfile(receiverWallet, 'alice.eth', [ + 'http://localhost:3000', + ]); + receiverOnGno = await mockUserProfile(receiverWallet, 'alice.gno', [ + 'http://localhost:3000', + ]); + rando = await mockUserProfile( + ethers.Wallet.createRandom(), + 'rando.eth', + ['http://localhost:3000'], + ); + + ds = await getMockDeliveryServiceProfile( + ethers.Wallet.createRandom(), + 'http://localhost:3000', + ); }); afterEach(() => { @@ -134,12 +174,13 @@ describe('WebSocketManager', () => { describe('isConnected', () => { it('returns true if name has one session', async () => { const mockedWeb3Provider = { - resolveName: (_: string) => Promise.resolve('0x123'), + resolveName: (_: string) => Promise.resolve(receiver.address), } as any; const mockedDatabase = { getAccount: () => Promise.resolve({ + account: receiver.address, token: 'old token that is not used anymore', createdAt: new Date().getTime(), }), @@ -155,9 +196,12 @@ describe('WebSocketManager', () => { client0 = await Client('http://localhost:4060', { auth: { account: { - ensName: 'bob.eth', + ensName: receiver.account.ensName, }, - token: generateAuthJWT('bob.eth', serverSecret), + token: generateAuthJWT( + receiver.account.ensName, + serverSecret, + ), }, }); @@ -173,17 +217,18 @@ describe('WebSocketManager', () => { ]); expect(socket0IsConnected).toBe(true); - const isConnected = await wsManager.isConnected('bob.eth'); + const isConnected = await wsManager.isConnected(receiver.address); expect(isConnected).toBe(true); }); it('returns true if name has at least one session', async () => { const mockedWeb3Provider = { - resolveName: (_: string) => Promise.resolve('0x123'), + resolveName: (_: string) => Promise.resolve(receiver.address), } as any; const mockedDatabase = { getAccount: () => Promise.resolve({ + account: receiver.address, token: 'old token that is not used anymore', createdAt: new Date().getTime(), }), @@ -199,17 +244,23 @@ describe('WebSocketManager', () => { client0 = await Client('http://localhost:4060', { auth: { account: { - ensName: 'bob.eth', + ensName: receiver.account.ensName, }, - token: generateAuthJWT('bob.eth', serverSecret), + token: generateAuthJWT( + receiver.account.ensName, + serverSecret, + ), }, }); client1 = await Client('http://localhost:4060', { auth: { account: { - ensName: 'bob.eth', + ensName: receiver.account.ensName, }, - token: generateAuthJWT('bob.eth', serverSecret), + token: generateAuthJWT( + receiver.account.ensName, + serverSecret, + ), }, }); @@ -234,22 +285,23 @@ describe('WebSocketManager', () => { expect(socket0IsConnected).toBe(true); expect(socket1IsConnected).toBe(true); - let isConnected = await wsManager.isConnected('bob.eth'); + let isConnected = await wsManager.isConnected(receiver.address); expect(isConnected).toBe(true); client0.close(); await wait(500); - isConnected = await wsManager.isConnected('bob.eth'); + isConnected = await wsManager.isConnected(receiver.address); expect(isConnected).toBe(true); }); it('returns false if name is unknown', async () => { const mockedWeb3Provider = { - resolveName: (_: string) => Promise.resolve('0x123'), + resolveName: (_: string) => Promise.resolve(receiver.address), } as any; const mockedDatabase = { getAccount: () => Promise.resolve({ + account: receiver.address, token: 'old token that is not used anymore', createdAt: new Date().getTime(), }), @@ -265,9 +317,12 @@ describe('WebSocketManager', () => { client0 = await Client('http://localhost:4060', { auth: { account: { - ensName: 'bob.eth', + ensName: receiver.account.ensName, }, - token: generateAuthJWT('bob.eth', serverSecret), + token: generateAuthJWT( + receiver.account.ensName, + serverSecret, + ), }, }); @@ -283,20 +338,42 @@ describe('WebSocketManager', () => { ]); expect(socket0IsConnected).toBe(true); - const isConnected = await wsManager.isConnected('alice.eth'); + const isConnected = await wsManager.isConnected(rando.address); expect(isConnected).toBe(false); }); it('keeps track of different independent sessions', async () => { const mockedWeb3Provider = { - resolveName: (_: string) => Promise.resolve('0x123'), + resolveName: (_: string) => { + if (_ === receiver.account.ensName) { + return Promise.resolve(receiver.address); + } + if (_ === sender.account.ensName) { + return Promise.resolve(sender.address); + } + if (_ === rando.account.ensName) { + return Promise.resolve(rando.address); + } + }, } as any; const mockedDatabase = { - getAccount: () => - Promise.resolve({ - token: 'old token that is not used anymore', - createdAt: new Date().getTime(), - }), + getAccount: (name: string) => { + if (name === receiver.account.ensName) { + return Promise.resolve({ + account: receiver.address, + }); + } + if (name === sender.account.ensName) { + return Promise.resolve({ + account: sender.address, + }); + } + if (name === rando.account.ensName) { + return Promise.resolve({ + account: rando.address, + }); + } + }, } as any; const wsManager = new WebSocketManager( @@ -309,27 +386,33 @@ describe('WebSocketManager', () => { client0 = await Client('http://localhost:4060', { auth: { account: { - ensName: 'bob.eth', + ensName: receiver.account.ensName, }, - token: generateAuthJWT('bob.eth', serverSecret), + token: generateAuthJWT( + receiver.account.ensName, + serverSecret, + ), }, }); client1 = await Client('http://localhost:4060', { auth: { account: { - ensName: 'alice.eth', + ensName: sender.account.ensName, }, - token: generateAuthJWT('alice.eth', serverSecret), + token: generateAuthJWT( + sender.account.ensName, + serverSecret, + ), }, }); client2 = await Client('http://localhost:4060', { auth: { account: { - ensName: 'vitalik.eth', + ensName: rando.account.ensName, }, - token: generateAuthJWT('vitalik.eth', serverSecret), + token: generateAuthJWT(rando.account.ensName, serverSecret), }, }); @@ -365,24 +448,25 @@ describe('WebSocketManager', () => { expect(socket1IsConnected).toBe(true); expect(socket2IsConnected).toBe(true); - expect(await wsManager.isConnected('bob.eth')).toBe(true); - expect(await wsManager.isConnected('alice.eth')).toBe(true); - expect(await wsManager.isConnected('vitalik.eth')).toBe(true); + expect(await wsManager.isConnected(receiver.address)).toBe(true); + expect(await wsManager.isConnected(sender.address)).toBe(true); + expect(await wsManager.isConnected(rando.address)).toBe(true); client1.close(); await wait(500); - expect(await wsManager.isConnected('bob.eth')).toBe(true); - expect(await wsManager.isConnected('alice.eth')).toBe(false); - expect(await wsManager.isConnected('vitalik.eth')).toBe(true); + expect(await wsManager.isConnected(receiver.address)).toBe(true); + expect(await wsManager.isConnected(sender.address)).toBe(false); + expect(await wsManager.isConnected(rando.address)).toBe(true); }); it('returns false after the user has closed all its connections', async () => { const mockedWeb3Provider = { - resolveName: (_: string) => Promise.resolve('0x123'), + resolveName: (_: string) => Promise.resolve(receiver.address), } as any; const mockedDatabase = { getAccount: () => Promise.resolve({ + account: receiver.address, token: 'old token that is not used anymore', createdAt: new Date().getTime(), }), @@ -398,9 +482,12 @@ describe('WebSocketManager', () => { client0 = await Client('http://localhost:4060', { auth: { account: { - ensName: 'bob.eth', + ensName: receiver.account.ensName, }, - token: generateAuthJWT('bob.eth', serverSecret), + token: generateAuthJWT( + receiver.account.ensName, + serverSecret, + ), }, }); @@ -416,12 +503,12 @@ describe('WebSocketManager', () => { ]); expect(socket0IsConnected).toBe(true); - let isConnected = await wsManager.isConnected('bob.eth'); + let isConnected = await wsManager.isConnected(receiver.address); expect(isConnected).toBe(true); client0.close(); await wait(500); - isConnected = await wsManager.isConnected('bob.eth'); + isConnected = await wsManager.isConnected(receiver.address); expect(isConnected).toBe(false); }); }); diff --git a/packages/delivery-service/src/ws/WebSocketManager.ts b/packages/delivery-service/src/ws/WebSocketManager.ts index 948e8888f..eceee4dee 100644 --- a/packages/delivery-service/src/ws/WebSocketManager.ts +++ b/packages/delivery-service/src/ws/WebSocketManager.ts @@ -39,11 +39,12 @@ export class WebSocketManager implements IWebSocketManager { /** * Checks if a user is connected. - * @param {string} ensName - The ENS name of the user. + * @param {string} address - The address of the user. * @returns {boolean} - Returns true if the user is connected with at least one socket, false otherwise. */ - public async isConnected(ensName: string) { - const connections = this.connections.get(ensName); + public async isConnected(address: string) { + const _address = ethers.utils.getAddress(address); + const connections = this.connections.get(_address); return !!(connections && connections.length > 0); } /** @@ -74,14 +75,17 @@ export class WebSocketManager implements IWebSocketManager { return; } //Get the old connections and add the new one - const oldConnections = this.connections.get(ensName) || []; - this.connections.set(ensName, [...oldConnections, connection]); + const oldConnections = this.connections.get(session.account) || []; + this.connections.set(session.account, [ + ...oldConnections, + connection, + ]); //Send the authorized event connection.emit(AUTHORIZED); - console.log('connection established for ', ensName); + console.log('connection established for ', session.account); //When the socket disconnects we want them no longer in our connections List connection.on('disconnect', () => { - console.log('connection closed for ', ensName); + console.log('connection closed for ', session.account); this.removeConnection(connection); }); } catch (e) { @@ -96,17 +100,29 @@ export class WebSocketManager implements IWebSocketManager { * @private * @param {Socket} connection - The socket connection instance. */ - private removeConnection(connection: Socket) { + private async removeConnection(connection: Socket) { const ensName = normalizeEnsName( connection.handshake.auth.account.ensName, ); - const connections = this.connections.get(ensName); + + //the resolved address for the name + const address = await this.web3Provider.resolveName(ensName); + if (!address) { + return; + } + //the connections the address has created previously + const connections = this.connections.get(address); + + //if there are no known connections we return if (!connections) { return; } + //we find the connection that has disconnected and remove it from the list const newConnections = connections.filter( (c) => c.id !== connection.id, ); - this.connections.set(ensName, newConnections); + + //we assign the list conaining all others connections an address might have to the list without the disconnected connection + this.connections.set(address, newConnections); } }