diff --git a/yarn-project/foundation/src/testing/index.ts b/yarn-project/foundation/src/testing/index.ts index e7616a77c78..b39430dd6f0 100644 --- a/yarn-project/foundation/src/testing/index.ts +++ b/yarn-project/foundation/src/testing/index.ts @@ -1,2 +1,3 @@ export * from './test_data.js'; export * from './snapshot_serializer.js'; +export * from './port_allocator.js'; diff --git a/yarn-project/foundation/src/testing/port_allocator.ts b/yarn-project/foundation/src/testing/port_allocator.ts new file mode 100644 index 00000000000..d1fa4c956f8 --- /dev/null +++ b/yarn-project/foundation/src/testing/port_allocator.ts @@ -0,0 +1,31 @@ +import net from 'net'; + +/** + * Get a random port that is free to use. + * Returns undefined if it fails to get a port. + * + * @attribution: adapted from https://stackoverflow.com/a/71178451 + * + * @returns a random port that is free to use. + */ +export function getRandomPort(): Promise { + return new Promise(resolve => { + const server = net.createServer(); + server.listen(0, () => { + const address = server.address(); + if (address && typeof address === 'object' && 'port' in address) { + const port = address.port; + server.close(() => { + resolve(port); + }); + } else { + server.close(() => { + resolve(undefined); + }); + } + }); + server.on('error', () => { + resolve(undefined); + }); + }); +} diff --git a/yarn-project/p2p/src/service/reqresp/p2p_client.integration.test.ts b/yarn-project/p2p/src/service/reqresp/p2p_client.integration.test.ts index ac1cab36f61..52f06c8700b 100644 --- a/yarn-project/p2p/src/service/reqresp/p2p_client.integration.test.ts +++ b/yarn-project/p2p/src/service/reqresp/p2p_client.integration.test.ts @@ -3,6 +3,7 @@ import { type ClientProtocolCircuitVerifier, type WorldStateSynchronizer, mockTx import { EthAddress } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; +import { getRandomPort } from '@aztec/foundation/testing'; import { type AztecKVStore } from '@aztec/kv-store'; import { type DataStoreConfig, openTmpStore } from '@aztec/kv-store/utils'; @@ -27,7 +28,7 @@ type Mockify = { const TEST_TIMEOUT = 80000; -const BOOT_NODE_UDP_PORT = 40400; +const DEFAULT_BOOT_NODE_UDP_PORT = 40400; async function createBootstrapNode(port: number) { const peerId = await createLibP2PPeerId(); const bootstrapNode = new BootstrapNode(); @@ -61,10 +62,12 @@ describe('Req Resp p2p client integration', () => { let kvStore: AztecKVStore; let worldStateSynchronizer: WorldStateSynchronizer; let proofVerifier: ClientProtocolCircuitVerifier; + let bootNodePort: number; const logger = createDebugLogger('p2p-client-integration-test'); const makeBootstrapNode = async (): Promise<[BootstrapNode, string]> => { - const bootstrapNode = await createBootstrapNode(BOOT_NODE_UDP_PORT); + bootNodePort = (await getRandomPort()) || DEFAULT_BOOT_NODE_UDP_PORT; + const bootstrapNode = await createBootstrapNode(bootNodePort); const enr = bootstrapNode.getENR().encodeTxt(); return [bootstrapNode, enr]; }; @@ -74,8 +77,9 @@ describe('Req Resp p2p client integration', () => { const peerIdPrivateKeys = generatePeerIdPrivateKeys(numberOfPeers); for (let i = 0; i < numberOfPeers; i++) { // Note these bindings are important - const addr = `127.0.0.1:${i + 1 + BOOT_NODE_UDP_PORT}`; - const listenAddr = `0.0.0.0:${i + 1 + BOOT_NODE_UDP_PORT}`; + const port = (await getRandomPort()) || bootNodePort + i + 1; + const addr = `127.0.0.1:${port}`; + const listenAddr = `0.0.0.0:${port}`; const config: P2PConfig & DataStoreConfig = { ...getP2PDefaultConfig(), p2pEnabled: true,