Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: WebRTC-Direct support for Node.js #2583

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
17 changes: 15 additions & 2 deletions packages/integration-tests/test/interop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { mplex } from '@libp2p/mplex'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { tcp } from '@libp2p/tcp'
import { tls } from '@libp2p/tls'
import { webRTCDirect } from '@libp2p/webrtc'
import { multiaddr } from '@multiformats/multiaddr'
import { execa } from 'execa'
import { path as p2pd } from 'go-libp2p'
Expand Down Expand Up @@ -45,6 +46,12 @@ async function createGoPeer (options: SpawnOptions): Promise<Daemon> {

if (options.noListen === true) {
opts.push('-noListenAddrs')

if (options.transport === 'webrtc-direct') {
// dialing webrtc-direct is broken in go-libp2p at the moment
// https://github.com/libp2p/go-libp2p/issues/2827
throw new UnsupportedError()
}
} else {
if (options.transport == null || options.transport === 'tcp') {
opts.push('-hostAddrs=/ip4/127.0.0.1/tcp/0')
Expand Down Expand Up @@ -132,7 +139,11 @@ async function createJsPeer (options: SpawnOptions): Promise<Daemon> {
addresses: {
listen: []
},
transports: [tcp(), circuitRelayTransport()],
transports: [
tcp(),
circuitRelayTransport(),
webRTCDirect()
],
streamMuxers: [],
connectionEncryption: [noise()],
connectionManager: {
Expand All @@ -143,12 +154,14 @@ async function createJsPeer (options: SpawnOptions): Promise<Daemon> {
if (options.noListen !== true) {
if (options.transport == null || options.transport === 'tcp') {
opts.addresses?.listen?.push('/ip4/127.0.0.1/tcp/0')
} else if (options.transport === 'webrtc-direct') {
opts.addresses?.listen?.push('/ip4/127.0.0.1/udp/0/webrtc-direct')
} else {
throw new UnsupportedError()
}
}

if (options.transport === 'webtransport' || options.transport === 'webrtc-direct') {
if (options.transport === 'webtransport') {
throw new UnsupportedError()
}

Expand Down
15 changes: 13 additions & 2 deletions packages/transport-webrtc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"doc-check": "aegir doc-check"
},
"dependencies": {
"@chainsafe/is-ip": "^2.0.2",
"@chainsafe/libp2p-noise": "^15.0.0",
"@libp2p/interface": "^1.4.1",
"@libp2p/interface-internal": "^1.2.3",
Expand All @@ -58,7 +59,10 @@
"@multiformats/mafmt": "^12.1.6",
"@multiformats/multiaddr": "^12.2.3",
"@multiformats/multiaddr-matcher": "^1.2.1",
"@peculiar/x509": "^1.11.0",
"any-signal": "^4.1.1",
"detect-browser": "^5.3.0",
"get-port": "^7.1.0",
"it-length-prefixed": "^9.0.4",
"it-protobuf-stream": "^1.1.3",
"it-pushable": "^3.2.3",
Expand All @@ -69,9 +73,12 @@
"p-defer": "^4.0.1",
"p-event": "^6.0.1",
"p-timeout": "^6.1.2",
"p-wait-for": "^5.0.2",
"protons-runtime": "^5.4.0",
"race-event": "^1.3.0",
"race-signal": "^1.0.2",
"react-native-webrtc": "^118.0.7",
"stun": "^2.1.0",
"uint8arraylist": "^2.4.8",
"uint8arrays": "^5.1.0"
},
Expand All @@ -96,10 +103,14 @@
"p-retry": "^6.2.0",
"protons": "^7.5.0",
"sinon": "^18.0.0",
"sinon-ts": "^2.0.0"
"sinon-ts": "^2.0.0",
"wherearewe": "^2.0.1"
},
"browser": {
"./dist/src/webrtc/index.js": "./dist/src/webrtc/index.browser.js"
"./dist/src/webrtc/index.js": "./dist/src/webrtc/index.browser.js",
"./dist/src/private-to-public/listener.js": "./dist/src/private-to-public/listener.browser.js",
"./dist/src/private-to-public/utils/get-rtcpeerconnection.js": "./dist/src/private-to-public/utils/get-rtcpeerconnection.browser.js",
"node:net": false
},
"react-native": {
"./dist/src/webrtc/index.js": "./dist/src/webrtc/index.react-native.js"
Expand Down
19 changes: 19 additions & 0 deletions packages/transport-webrtc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,25 @@ export interface DataChannelOptions {
openTimeout?: number
}

/**
* PEM format server certificate and private key
*/
export interface TransportCertificate {
/**
* The private key for the certificate in PEM format
*/
privateKey: string
/**
* PEM format certificate
*/
pem: string

/**
* The hash of the certificate
*/
certhash: string
}

/**
* @param {WebRTCTransportDirectInit} init - WebRTC direct transport configuration
* @param init.dataChannel - DataChannel configurations
Expand Down
7 changes: 4 additions & 3 deletions packages/transport-webrtc/src/maconn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,13 @@ export class WebRTCMultiaddrConnection implements MultiaddrConnection {
this.timeline = init.timeline
this.peerConnection = init.peerConnection

const initialState = this.peerConnection.connectionState
const peerConnection = this.peerConnection
const initialState = peerConnection.connectionState

this.peerConnection.onconnectionstatechange = () => {
this.log.trace('peer connection state change', this.peerConnection.connectionState, 'initial state', initialState)
this.log.trace('peer connection state change', peerConnection.connectionState, 'initial state', initialState)

if (this.peerConnection.connectionState === 'disconnected' || this.peerConnection.connectionState === 'failed' || this.peerConnection.connectionState === 'closed') {
if (peerConnection.connectionState === 'disconnected' || peerConnection.connectionState === 'failed' || peerConnection.connectionState === 'closed') {
// nothing else to do but close the connection
this.timeline.close = Date.now()
}
Expand Down
14 changes: 10 additions & 4 deletions packages/transport-webrtc/src/muxer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class DataChannelMuxerFactory implements StreamMuxerFactory {
this.metrics = init.metrics
this.protocol = init.protocol ?? PROTOCOL
this.dataChannelOptions = init.dataChannelOptions ?? {}
this.log = components.logger.forComponent('libp2p:webrtc:datachannelmuxerfactory')
this.log = components.logger.forComponent('libp2p:webrtc:muxerfactory')

// store any datachannels opened before upgrade has been completed
this.peerConnection.ondatachannel = ({ channel }) => {
Expand Down Expand Up @@ -155,11 +155,14 @@ export class DataChannelMuxer implements StreamMuxer {
return
}

// cannot access `.id` property after close as node-datachannel throws
const id = channel.id

const stream = createStream({
channel,
direction: 'inbound',
onEnd: () => {
this.log('incoming channel %s ended with state %s', channel.id, channel.readyState)
this.log('incoming channel %s ended with state %s', id, channel.readyState)
this.#onStreamEnd(stream, channel)
},
logger: this.logger,
Expand Down Expand Up @@ -239,16 +242,19 @@ export class DataChannelMuxer implements StreamMuxer {
sink: Sink<Source<Uint8Array | Uint8ArrayList>, Promise<void>> = nopSink

newStream (): Stream {
// The spec says the label SHOULD be an empty string: https://github.com/libp2p/specs/blob/master/webrtc/README.md#rtcdatachannel-label
// The spec says the label MUST be an empty string: https://github.com/libp2p/specs/blob/master/webrtc/README.md#rtcdatachannel-label
const channel = this.peerConnection.createDataChannel('')

this.log.trace('opened outgoing datachannel with channel id %s', channel.id)

// cannot access `.id` property after close as node-datachannel throws
const id = channel.id

const stream = createStream({
channel,
direction: 'outbound',
onEnd: () => {
this.log('outgoing channel %s ended with state %s', channel.id, channel.readyState)
this.log('outgoing channel %s ended with state %s', id, channel.readyState)
this.#onStreamEnd(stream, channel)
},
logger: this.logger,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const UFRAG_PREFIX = 'libp2p+webrtc+v1/'

// https://gist.github.com/mondain/b0ec1cf5f60ae726202e
export const DEFAULT_STUN_SERVERS = [
'stun.l.google.com:19302',
'global.stun.twilio.com:3478',
'stun.cloudflare.com:3478'
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { TypedEventEmitter } from '@libp2p/interface'
import { unimplemented } from '../error.js'
import type { PeerId, ListenerEvents, Listener } from '@libp2p/interface'
import type { TransportManager } from '@libp2p/interface-internal'
import type { Multiaddr } from '@multiformats/multiaddr'

export interface WebRTCDirectListenerComponents {
peerId: PeerId
transportManager: TransportManager
}

export interface WebRTCDirectListenerInit {
shutdownController: AbortController
}

export class WebRTCDirectListener extends TypedEventEmitter<ListenerEvents> implements Listener {
async listen (): Promise<void> {
throw unimplemented('WebRTCTransport.createListener')
}

getAddrs (): Multiaddr[] {
return []
}

async close (): Promise<void> {

}
}
Loading
Loading