diff --git a/.github/workflows/devp2p-build.yml b/.github/workflows/devp2p-build.yml index c7fa3d2bd0..d8b2465002 100644 --- a/.github/workflows/devp2p-build.yml +++ b/.github/workflows/devp2p-build.yml @@ -36,10 +36,11 @@ jobs: - run: npm ci --omit=peer working-directory: ${{github.workspace}} - - uses: nick-fields/retry@v2 + - run: npm run coverage + + - uses: codecov/codecov-action@v3 with: - timeout_minutes: 10 - max_attempts: 3 - command: cd ${{github.workspace}}/packages/devp2p && npm run coverage + files: ${{ env.cwd }}/coverage/lcov.info + flags: devp2p - run: npm run lint diff --git a/.github/workflows/genesis-build.yml b/.github/workflows/genesis-build.yml index 18ccaf6b5c..5428580a32 100644 --- a/.github/workflows/genesis-build.yml +++ b/.github/workflows/genesis-build.yml @@ -39,4 +39,4 @@ jobs: working-directory: ${{github.workspace}} - run: npm run lint - - run: npm run test:node # Only run node tests for now until vitest browser test issues are sorted out + - run: npm run test:node # Only run node tests for now until vitest browser test issues are sorted out \ No newline at end of file diff --git a/.github/workflows/rlp-build.yml b/.github/workflows/rlp-build.yml index c7030e8a4b..dcb829e707 100644 --- a/.github/workflows/rlp-build.yml +++ b/.github/workflows/rlp-build.yml @@ -37,8 +37,4 @@ jobs: working-directory: ${{github.workspace}} - run: npm run lint - - run: npm run coverage - - uses: codecov/codecov-action@v3 - with: - files: ${{ env.cwd }}/coverage/lcov.info - flags: rlp + diff --git a/packages/devp2p/src/rlpx/rlpx.ts b/packages/devp2p/src/rlpx/rlpx.ts index a7f5ccefbd..ee53e0b698 100644 --- a/packages/devp2p/src/rlpx/rlpx.ts +++ b/packages/devp2p/src/rlpx/rlpx.ts @@ -225,13 +225,11 @@ export class RLPx { peer.events.once('connect', () => { let msg = `handshake with ${socket.remoteAddress}:${socket.remotePort} was successful` - // @ts-ignore - if (peer._eciesSession._gotEIP8Auth === true) { + if (peer['_eciesSession']['_gotEIP8Auth'] === true) { msg += ` (peer eip8 auth)` } - // @ts-ignore - if (peer._eciesSession._gotEIP8Ack === true) { + if (peer['_eciesSession']['_gotEIP8Ack'] === true) { msg += ` (peer eip8 ack)` } this._debug(msg) diff --git a/packages/devp2p/test/rlpx.spec.ts b/packages/devp2p/test/rlpx.spec.ts new file mode 100644 index 0000000000..457bab846e --- /dev/null +++ b/packages/devp2p/test/rlpx.spec.ts @@ -0,0 +1,176 @@ +// Tests written with help from CodiumAI + +import { Common } from '@ethereumjs/common' +import { equalsBytes, randomBytes } from '@ethereumjs/util' +import assert from 'assert' +import { secp256k1 } from 'ethereum-cryptography/secp256k1.js' +import EventEmitter from 'events' +import { describe, expect, it, vi } from 'vitest' + +import { RLPx, pk2id } from '../src/index.js' + +import type { RLPxOptions } from '../src/index.js' + +const privateKey = randomBytes(32) +describe('RLPx', () => { + it('should create an instance of RLPx with the given private key and options', () => { + const options: RLPxOptions = { + timeout: 10000, + maxPeers: 10, + clientId: new Uint8Array([6, 7, 8, 9, 10]), + capabilities: [], + common: new Common({ chain: 1 }), + } + + const rlpx = new RLPx(privateKey, options) + + expect(rlpx).toBeInstanceOf(RLPx) + expect(rlpx.events).toBeInstanceOf(EventEmitter) + expect(rlpx['_privateKey']).toEqual(privateKey) + expect(rlpx.id).toEqual(pk2id(secp256k1.getPublicKey(privateKey, false))) + expect(rlpx['_timeout']).toEqual(options.timeout) + expect(rlpx['_maxPeers']).toEqual(options.maxPeers) + expect(rlpx.clientId).toEqual(options.clientId) + expect(rlpx['_remoteClientIdFilter']).toBeUndefined() + expect(rlpx['_capabilities']).toEqual(options.capabilities) + assert.deepEqual(rlpx['_common'], options.common) + expect(rlpx['_listenPort']).toBeNull() + expect(rlpx['_dpt']).toBeNull() + expect(rlpx['_peersQueue']).toEqual([]) + expect(rlpx['_peers']).toBeInstanceOf(Map) + expect(rlpx['_refillIntervalId']).toBeDefined() + expect(rlpx['_refillIntervalSelectionCounter']).toEqual(0) + }) + + it('should start listening for incoming connections', () => { + const options: RLPxOptions = { + timeout: 10000, + maxPeers: 10, + clientId: new Uint8Array([6, 7, 8, 9, 10]), + capabilities: [], + common: new Common({ chain: 1 }), + } + + const rlpx = new RLPx(privateKey, options) + const mockServer = { + listen: vi.fn(), + } + rlpx['_server'] = mockServer as any + + rlpx.listen(30303) + + expect(mockServer.listen).toHaveBeenCalledWith(30303) + }) + + it('should connect to a peer', async () => { + vi.mock('net', () => { + const Socket = vi.fn().mockImplementation(() => { + return { + on: vi.fn(), + once: vi.fn(), + setTimeout: () => {}, + connect: vi.fn().mockImplementation(() => { + return 'mocked resolve!' + }), + } + }) + return { + createServer: () => { + return { + once: vi.fn(), + on: vi.fn(), + address: () => '0.0.0.0', + } + }, + Socket, + } + }) + vi.mock('../src/util.js', async () => { + const util: any = await vi.importActual('../src/util.js') + return { + ...util, + createDeferred: vi.fn().mockImplementation(() => { + return { + promise: { + resolve: vi.fn().mockResolvedValue(() => 'mocked resolve!'), + reject: vi.fn(), + }, + } + }), + } + }) + const options: RLPxOptions = { + timeout: 10000, + maxPeers: 10, + clientId: new Uint8Array([6, 7, 8, 9, 10]), + capabilities: [], + common: new Common({ chain: 1 }), + } + + const rlpx = new RLPx(privateKey, options) + const mockServer = { + listen: vi.fn(), + once: vi.fn(), + on: vi.fn(), + } + rlpx['_server'] = mockServer as any + const mockPeer = { + tcpPort: 30303, + address: '127.0.0.1', + id: new Uint8Array([11, 12, 13, 14, 15]), + } + + const mockOnConnect = vi.spyOn(rlpx, '_onConnect').mockImplementation((_, peerId) => { + assert(equalsBytes(peerId as Uint8Array, mockPeer.id), 'received correct peerId') + }) + await rlpx.connect(mockPeer) + + expect(mockOnConnect).toHaveBeenCalled() + }) + + it('should throw an error if already connected to a peer', async () => { + const options: RLPxOptions = { + timeout: 10000, + maxPeers: 10, + clientId: new Uint8Array([6, 7, 8, 9, 10]), + capabilities: [], + common: new Common({ chain: 1 }), + } + const rlpx = new RLPx(privateKey, options) + const mockPeer = { + tcpPort: 30303, + address: '127.0.0.1', + id: new Uint8Array([11, 12, 13, 14, 15]), + } + rlpx['_peers'].set('0b0c0d0e0f', {} as any) + + try { + await rlpx.connect(mockPeer) + assert.fail('should throw') + } catch (err: any) { + assert.equal(err.message, 'Already connected') + } + }) + + it('should return open slots and open queue slots', () => { + const options: RLPxOptions = { + timeout: 10000, + maxPeers: 10, + clientId: new Uint8Array([6, 7, 8, 9, 10]), + capabilities: [], + common: new Common({ chain: 1 }), + } + const rlpx = new RLPx(privateKey, options) + + assert.equal( + rlpx['_getOpenSlots'](), + 10, + 'returns default number of open slots (i.e. `max_peers`) on startup' + ) + assert.equal( + rlpx['_getOpenQueueSlots'](), + 20, + 'returns default number of open queue slots on startup' + ) + }) +})