Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions packages/integration-tests/.aegir.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { execa } from 'execa'
import fs from 'node:fs'
import net from 'node:net'
import { randomUUID } from 'node:crypto'
import pDefer from 'p-defer'

/** @type {import('aegir').PartialOptions} */
Expand Down Expand Up @@ -169,11 +172,11 @@ async function createGoLibp2pRelay () {
const { defaultLogger } = await import('@libp2p/logger')

const log = defaultLogger().forComponent('go-libp2p')
const controlPort = Math.floor(Math.random() * (50000 - 10000 + 1)) + 10000
const apiAddr = multiaddr(`/ip4/127.0.0.1/tcp/${controlPort}`)
const { controlSocketPath, apiAddr, daemonListenAddr } = await getControlEndpoint(multiaddr)
const deferred = pDefer()

const proc = execa(p2pd(), [
`-listen=${apiAddr.toString()}`,
`-listen=${daemonListenAddr}`,
// listen on TCP, WebSockets and WebTransport
'-hostAddrs=/ip4/127.0.0.1/tcp/0,/ip4/127.0.0.1/tcp/0/ws,/ip4/127.0.0.1/udp/0/quic-v1/webtransport',
'-noise=true',
Expand All @@ -185,8 +188,17 @@ async function createGoLibp2pRelay () {
GOLOG_LOG_LEVEL: 'debug'
}
})

proc.catch(() => {
// go-libp2p daemon throws when killed
}).finally(() => {
if (controlSocketPath != null) {
fs.rmSync(controlSocketPath, { force: true })
}
})

proc.once('exit', code => {
deferred.reject(new Error(`go-libp2p daemon exited before startup (code: ${code ?? 'unknown'})`))
})

proc.stdout?.on('data', (buf) => {
Expand All @@ -202,6 +214,7 @@ async function createGoLibp2pRelay () {

log(str)
})

await deferred.promise

const daemonClient = createClient(apiAddr)
Expand All @@ -214,3 +227,52 @@ async function createGoLibp2pRelay () {
proc
}
}

async function getControlEndpoint (multiaddr) {
if (process.platform === 'win32') {
const controlPort = await getAvailablePort()
const apiAddr = multiaddr(`/ip4/127.0.0.1/tcp/${controlPort}`)

return {
controlPort,
apiAddr,
daemonListenAddr: apiAddr.toString()
}
}

const controlSocketPath = `/tmp/p2pd-${randomUUID()}.sock`
const apiAddr = multiaddr(`/unix/${encodeURIComponent(controlSocketPath)}`)

return {
controlSocketPath,
apiAddr,
daemonListenAddr: `/unix${controlSocketPath}`
}
}

async function getAvailablePort () {
return new Promise((resolve, reject) => {
const server = net.createServer()

server.once('error', reject)
server.listen(0, '127.0.0.1', () => {
const addr = server.address()

if (addr == null || typeof addr === 'string') {
server.close(() => {
reject(new Error('could not allocate control port'))
})
return
}

server.close(err => {
if (err != null) {
reject(err)
return
}

resolve(addr.port)
})
})
})
}
81 changes: 76 additions & 5 deletions packages/integration-tests/test/interop.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import fs from 'fs'
import { randomUUID } from 'node:crypto'
import net from 'node:net'
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2'
Expand Down Expand Up @@ -38,14 +40,20 @@ import type { Libp2pOptions, ServiceFactoryMap } from 'libp2p'
* ```
*/

interface ControlEndpoint {
controlSocketPath?: string
controlPort?: number
apiAddr: ReturnType<typeof multiaddr>
daemonListenAddr: string
}

async function createGoPeer (options: SpawnOptions): Promise<Daemon> {
const controlPort = Math.floor(Math.random() * (50000 - 10000 + 1)) + 10000
const apiAddr = multiaddr(`/ip4/127.0.0.1/tcp/${controlPort}`)
const { controlSocketPath, controlPort, apiAddr, daemonListenAddr } = await getControlEndpoint()

const log = logger(`go-libp2p:${controlPort}`)
const log = logger(`go-libp2p:${controlSocketPath ?? controlPort}`)

const opts = [
`-listen=${apiAddr.toString()}`
`-listen=${daemonListenAddr}`
]

if (options.noListen === true) {
Expand Down Expand Up @@ -92,13 +100,21 @@ async function createGoPeer (options: SpawnOptions): Promise<Daemon> {
opts.push('-muxer=yamux')
}

const deferred = pDefer()
const deferred = pDefer<void>()
const proc = execa(p2pd(), opts, {
env: {
GOLOG_LOG_LEVEL: 'debug'
}
})

proc.catch(() => {
// go-libp2p daemon throws when killed
})

proc.once('exit', code => {
deferred.reject(new Error(`go-libp2p daemon exited before startup (code: ${code ?? 'unknown'})`))
})

proc.stdout?.on('data', (buf: Buffer) => {
const str = buf.toString()
log(str)
Expand All @@ -119,8 +135,63 @@ async function createGoPeer (options: SpawnOptions): Promise<Daemon> {
client: createClient(apiAddr),
stop: async () => {
proc.kill()

if (controlSocketPath != null) {
fs.rmSync(controlSocketPath, { force: true })
}
}
}
}

async function getControlEndpoint (): Promise<ControlEndpoint> {
// Use tcp control ports on windows
if (process.platform === 'win32') {
const controlPort = await getAvailablePort()
const apiAddr = multiaddr(`/ip4/127.0.0.1/tcp/${controlPort}`)

return {
controlPort,
apiAddr,
daemonListenAddr: apiAddr.toString()
}
}

// Use unix domain sockets on non-windows - avoids port clashes
const controlSocketPath = `/tmp/p2pd-${randomUUID()}.sock`
const apiAddr = multiaddr(`/unix/${encodeURIComponent(controlSocketPath)}`)

return {
controlSocketPath,
apiAddr,
daemonListenAddr: `/unix${controlSocketPath}`
}
}

async function getAvailablePort (): Promise<number> {
return new Promise((resolve, reject) => {
const server = net.createServer()

server.once('error', reject)
server.listen(0, '127.0.0.1', () => {
const addr = server.address()

if (addr == null || typeof addr === 'string') {
server.close(() => {
reject(new Error('could not allocate control port'))
})
return
}

server.close(err => {
if (err != null) {
reject(err)
return
}

resolve(addr.port)
})
})
})
}

async function createJsPeer (options: SpawnOptions): Promise<Daemon> {
Expand Down