Skip to content

Commit

Permalink
feat: add circuit relay server
Browse files Browse the repository at this point in the history
- Adds the ability to start a circuit relay server for testing webrtc
- Renames server/client listener/dialer
- Uses env vars for config because browser bundles don't get node internals or cli args
- Adds pw-test for running in browsers
  • Loading branch information
achingbrain committed Sep 5, 2024
1 parent 50b451f commit 89bb195
Show file tree
Hide file tree
Showing 18 changed files with 12,804 additions and 117 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/perf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,18 @@ jobs:
name: Retrieve client's IP
run: terraform output -raw client_ip
working-directory: perf/terraform/configs/local
- id: relay
name: Retrieve relay's IP
run: terraform output -raw relay_ip
working-directory: perf/terraform/configs/local
- name: Download dependencies
run: npm ci
working-directory: perf/runner
- name: Run tests
env:
SERVER_IP: ${{ steps.server.outputs.stdout }}
CLIENT_IP: ${{ steps.client.outputs.stdout }}
run: npm run start -- --client-public-ip $CLIENT_IP --server-public-ip $SERVER_IP
run: npm run start -- --client-public-ip $CLIENT_IP --server-public-ip $SERVER_IP --relay-public-ip $RELAY_IP
working-directory: perf/runner
- name: Push
if: github.event.inputs.push == 'true'
Expand Down
3 changes: 2 additions & 1 deletion perf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Benchmark results can be visualized with https://observablehq.com/@libp2p-worksp
4. `terraform apply`
5. `CLIENT_IP=$(terraform output -raw client_ip)`
6. `SERVER_IP=$(terraform output -raw server_ip)`
7. `RELAY_IP=$(terraform output -raw relay_ip)`

**Notes**
- While running terraform you may encounter the following error:
Expand All @@ -52,7 +53,7 @@ Given you have provisioned your infrastructure, you can now build and run the li

1. `cd runner`
2. `npm ci`
3. `npm run start -- --client-public-ip $CLIENT_IP --server-public-ip $SERVER_IP`
3. `npm run start -- --client-public-ip $CLIENT_IP --server-public-ip $SERVER_IP --relay-public-ip $RELAY_IP`
* Note: The default number of iterations that perf will run is 10; desired iterations can be set with the `--iterations <value>` option.

### Deprovision infrastructure
Expand Down
16 changes: 8 additions & 8 deletions perf/impl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ QUIC_GO_SUBDIRS := $(wildcard quic-go/*/.)
JS_SUBDIRS := $(wildcard js-libp2p/*/.)

all: $(RUST_SUBDIRS) $(GO_SUBDIRS) $(HTTPS_SUBDIRS) $(QUIC_GO_SUBDIRS) $(JS_SUBDIRS)
$(RUST_SUBDIRS):
$(MAKE) -C $@
$(GO_SUBDIRS):
$(MAKE) -C $@
$(HTTPS_SUBDIRS):
$(MAKE) -C $@
$(QUIC_GO_SUBDIRS):
$(MAKE) -C $@
# $(RUST_SUBDIRS):
# $(MAKE) -C $@
# $(GO_SUBDIRS):
# $(MAKE) -C $@
# $(HTTPS_SUBDIRS):
# $(MAKE) -C $@
# $(QUIC_GO_SUBDIRS):
# $(MAKE) -C $@
$(JS_SUBDIRS):
$(MAKE) -C $@

Expand Down
12 changes: 12 additions & 0 deletions perf/impl/js-libp2p/v1.0-webrtc-16kb/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
DOCKER_IMAGE := node:20-alpine
DOCKER_RUN := docker run --rm -v "$(shell pwd)":/usr/src/myapp -w /usr/src/myapp $(DOCKER_IMAGE)

all: perf

perf:
$(DOCKER_RUN) npm ci

clean:
rm -rf node_modules

.PHONY: all clean perf
60 changes: 60 additions & 0 deletions perf/impl/js-libp2p/v1.0-webrtc-16kb/dialer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { createLibp2p } from 'libp2p'
import { perf } from '@libp2p/perf'
import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
import { webRTC } from '@libp2p/webrtc'
import { tcp } from '@libp2p/tcp'
import { identify } from '@libp2p/identify'
import { multiaddr } from '@multiformats/multiaddr'
import { webSockets } from '@libp2p/websockets'
import { all } from '@libp2p/websockets/filters'

const transport = process.env.TRANSPORT
const listenerAddress = process.env.LISTENER_ADDRESS
const uploadBytes = Number(process.env.UPLOAD_BYTES)
const downloadBytes = Number(process.env.DOWNLOAD_BYTES)

const config = {
transports: [],
streamMuxers: [
yamux()
],
connectionEncryption: [
noise()
],
connectionManager: {
minConnections: 0
},
services: {
perf: perf(),
identify: identify()
}
}

if (transport === 'tcp') {
config.transports.push(tcp())
} else if (transport === 'webrtc') {
config.transports.push(circuitRelayTransport())
config.transports.push(webRTC())
config.transports.push(webSockets({
filter: all
}))
} else if (transport === 'ws') {
config.transports.push(webSockets({
filter: all
}))
} else if (transport === 'wss') {
config.transports.push(webSockets({
filter: all
}))
}

const node = await createLibp2p(config)

for await (const output of node.services.perf.measurePerformance(multiaddr(listenerAddress), uploadBytes, downloadBytes)) {
// eslint-disable-next-line no-console
console.log(JSON.stringify(output))
}

process.exit(0)
108 changes: 108 additions & 0 deletions perf/impl/js-libp2p/v1.0-webrtc-16kb/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { tcp } from '@libp2p/tcp'
import { multiaddr } from '@multiformats/multiaddr'
import { createLibp2p } from 'libp2p'
import { perf } from '@libp2p/perf'
import { parseArgs } from 'node:util'

const argv = parseArgs({
options: {
'run-server': {
type: 'string',
default: 'false'
},
'server-address': {
type: 'string'
},
transport: {
type: 'string',
default: 'tcp'
},
'upload-bytes': {
type: 'string',
default: '0'
},
'download-bytes': {
type: 'string',
default: '0'
}
}
})

/**
* @param {boolean} runServer
* @param {string} serverIpAddress
* @param {string} transport
* @param {number} uploadBytes
* @param {number} downloadBytes
*/
export async function main (runServer, serverIpAddress, transport, uploadBytes, downloadBytes) {
const { host, port } = splitHostPort(serverIpAddress)

const config = {
transports: [
tcp()
],
streamMuxers: [
yamux()
],
connectionEncryption: [
noise()
],
connectionManager: {
minConnections: 0
},
services: {
perf: perf()
}
}

if (runServer) {
Object.assign(config, {
addresses: {
listen: [
// #TODO: right now we only support tcp
`/ip4/${host}/tcp/${port}`
]
}
})
}

const node = await createLibp2p(config)

await node.start()

if (!runServer) {
for await (const output of node.services.perf.measurePerformance(multiaddr(`/ip4/${host}/tcp/${port}`), uploadBytes, downloadBytes)) {
// eslint-disable-next-line no-console
console.log(JSON.stringify(output))
}

await node.stop()
}
}

/**
* @param {string} address
* @returns { host: string, port?: string }
*/
function splitHostPort (address) {
try {
const parts = address.split(':')
const host = parts[0]
const port = parts[1]
return {
host,
port
}
} catch (error) {
throw Error('Invalid server address')
}
}

main(argv.values['run-server'] === 'true', argv.values['server-address'], argv.values.transport, Number(argv.values['upload-bytes']), Number(argv.values['download-bytes'])).catch((err) => {
// eslint-disable-next-line no-console
console.error(err)
process.exit(1)
})
92 changes: 92 additions & 0 deletions perf/impl/js-libp2p/v1.0-webrtc-16kb/listener.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { createLibp2p } from 'libp2p'
import { perf } from '@libp2p/perf'
import { WebRTC, TCP } from '@multiformats/multiaddr-matcher'
import { webSockets } from '@libp2p/websockets'
import { all } from '@libp2p/websockets/filters'
import { identify } from '@libp2p/identify'
import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
import { webRTC } from '@libp2p/webrtc'
import { tcp } from '@libp2p/tcp'

const transport = process.env.TRANSPORT
const relayAddress = process.env.RELAY_ADDRESS
const listenPort = process.env.LISTEN_PORT
const externalIp = process.env.EXTERNAL_IP

const config = {
transports: [],
streamMuxers: [
yamux()
],
connectionEncryption: [
noise()
],
connectionManager: {
minConnections: 0
},
services: {
perf: perf(),
identify: identify()
},
connectionGater: {
denyDialMultiaddr: () => false
}
}

if (transport === 'tcp') {
config.transports.push(tcp())

config.addresses = {
listen: [
`/ip4/0.0.0.0/tcp/${listenPort}`
],
announce: [
`/ip4/${externalIp}/tcp/${listenPort}`
]
}
} else if (transport === 'webrtc') {
config.transports.push(circuitRelayTransport())
config.transports.push(webRTC())
config.transports.push(webSockets({
filter: all
}))

config.addresses = {
listen: [
`${relayAddress}/p2p-circuit`,
'/webrtc'
],
announce: [
`${relayAddress}/p2p-circuit/webrtc`,
]
}
} else if (transport === 'ws') {
config.transports.push(webSockets({
filter: all
}))
} else if (transport === 'wss') {
config.transports.push(webSockets({
filter: all
}))
}

const node = await createLibp2p(config)
let multiaddr

if (transport === 'tcp') {
multiaddr = node.getMultiaddrs()
.filter(ma => TCP.matches(ma))
.map(ma => ma.toString())
.pop()
} else if (transport === 'webrtc') {
multiaddr = node.getMultiaddrs()
.filter(ma => WebRTC.matches(ma))
.map(ma => ma.toString())
.pop()
}

// only need to print out one multiaddr because the runner will switch the
// private IP for the public one before passing it to the client/server
console.info(multiaddr)
Loading

0 comments on commit 89bb195

Please sign in to comment.