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

Develop #21

Merged
merged 24 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
Expand Down
24 changes: 24 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"version": "0.2.0",

"configurations": [
{
"type": "node",
"request": "attach",
"name": "Node: Attach to Process",
"processId": "${command:PickProcess}",
"restart": true,

// "protocol": "inspector",
"stopOnEntry": "false",
// "runToMain": "false",
"skipFiles": [
// Node.js internal core modules
"<node_internals>/**",

// Ignore all dependencies (optional)
"${workspaceFolder}/node_modules/**"
]
}
]
}
65 changes: 65 additions & 0 deletions examples/connector-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { createLogger } from '../src/utils/logger'
// @ts-nocheck
import webRTC from '@koush/wrtc'
import { ConnectorClient } from '../src/radix-connect-webrtc'
import { NodeWebSocket } from '../src/dependencies/websocket'
import type { Dependencies } from '../src/_types'

const logger = createLogger(0)

const NodeWebRTC = () => ({
RTCIceCandidate: webRTC.RTCIceCandidate,
RTCSessionDescription: webRTC.RTCSessionDescription,
RTCPeerConnection: webRTC.RTCPeerConnection,
})

const dependencies: Dependencies = {
WebRTC: NodeWebRTC(),
WebSocket: NodeWebSocket(),
}

const connector = ConnectorClient({
source: 'extension',
target: 'wallet',
logger: logger,
isInitiator: true,
dependencies,
negotiationTimeout: 60000,
})

connector.setConnectionConfig({
signalingServerBaseUrl:
'wss://signaling-server-dev.rdx-works-main.extratools.works',
iceTransportPolicy: 'all',
turnServers: [
{
urls: 'turn:standard.relay.metered.ca:80',
username: '51253affa7c2960189ce8cb6',
credential: '3HWkp3Wgg2cujD2g',
},
{
urls: 'turn:standard.relay.metered.ca:80?transport=tcp',
username: '51253affa7c2960189ce8cb6',
credential: '3HWkp3Wgg2cujD2g',
},
{
urls: 'turn:standard.relay.metered.ca:443',
username: '51253affa7c2960189ce8cb6',
credential: '3HWkp3Wgg2cujD2g',
},
{
urls: 'turns:standard.relay.metered.ca:443?transport=tcp',
username: '51253affa7c2960189ce8cb6',
credential: '3HWkp3Wgg2cujD2g',
},
],
})

connector.setConnectionPassword(
Buffer.from(
'c23a3446758d932eab6996a68680bbb3cce08a2b46fa28e1384f6bfaa0f562f8',
'hex',
),
)

connector.connect()
141 changes: 141 additions & 0 deletions examples/peer-connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { createLogger } from '../src/utils/logger'
// @ts-nocheck
import webRTC from '@koush/wrtc'
import type { WebRTC } from '../src/dependencies/webrtc'

const logger = createLogger(0)

const NodeWebRTC = () => ({
RTCIceCandidate: webRTC.RTCIceCandidate,
RTCSessionDescription: webRTC.RTCSessionDescription,
RTCPeerConnection: webRTC.RTCPeerConnection,
})

const webrtc: WebRTC = NodeWebRTC()

const pc1 = new webrtc.RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302',
},
{
urls: 'stun:stun1.l.google.com:19302',
},
{
urls: 'stun:stun2.l.google.com:19302',
},
{
urls: 'stun:stun3.l.google.com:19302',
},
{
urls: 'stun:stun4.l.google.com:19302',
},
],
})

const dc1 = pc1.createDataChannel('test')

const pc2 = new webrtc.RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302',
},
{
urls: 'stun:stun1.l.google.com:19302',
},
{
urls: 'stun:stun2.l.google.com:19302',
},
{
urls: 'stun:stun3.l.google.com:19302',
},
{
urls: 'stun:stun4.l.google.com:19302',
},
],
})

const dc2 = pc2.createDataChannel('test')

const pc1Candidates: any[] = []
const pc2Candidates: any[] = []

pc1.onicecandidate = async ({ candidate }) => {
// logger.info('adding pc2 candidate')
if (candidate) pc1Candidates.push(candidate)
}

pc2.onicecandidate = async ({ candidate }) => {
// logger.info('adding pc1 candidate')
if (candidate) pc2Candidates.push(candidate)
}

pc1.onnegotiationneeded = () => logger.info(`pc1 negotiationneeded`)

pc1.onconnectionstatechange = () =>
logger.info(`pc1 onconnectionstatechange: ${pc1.connectionState}`)

pc1.oniceconnectionstatechange = () =>
logger.info(`pc1 oniceconnectionstatechange: ${pc1.iceConnectionState}`)

pc2.onnegotiationneeded = () => logger.info(`pc2 negotiationneeded`)
pc2.onconnectionstatechange = () =>
logger.info(`pc2 onconnectionstatechange: ${pc2.connectionState}`)
pc2.oniceconnectionstatechange = () =>
logger.info(`pc2 oniceconnectionstatechange: ${pc2.iceConnectionState}`)

dc1.onopen = () => logger.info(`pc1 datachannel: ${dc1.readyState}`)
dc2.onopen = () => logger.info(`pc2 datachannel: ${dc2.readyState}`)

const offer = await pc1.createOffer()
logger.info('pc1 createOfferSuccess')
await pc1.setLocalDescription(offer)
logger.info('pc1 setLocalDescriptionSuccess')

await pc2.setRemoteDescription(offer)
logger.info('pc2 setRemoteDescriptionSuccess')
const answer = await pc2.createAnswer()
logger.info('pc2 createAnswerSuccess')
await pc2.setLocalDescription(answer)
logger.info('pc2 setLocalDescriptionSuccess')

await pc1.setRemoteDescription(answer)
logger.info('pc1 setRemoteDescriptionSuccess')

for (const candidate of pc2Candidates) {
await pc1.addIceCandidate(candidate)
logger.info('pc1 addIceCandidateSuccess')
}

for (const candidate of pc1Candidates) {
await pc2.addIceCandidate(candidate)
logger.info('pc2 addIceCandidateSuccess')
}

// const dependencies: Dependencies = {
// WebRTC: NodeWebRTC(),
// WebSocket: NodeWebSocket(),
// }

// const connector = ConnectorClient({
// source: 'extension',
// target: 'wallet',
// logger: logger,
// isInitiator: true,
// dependencies,
// negotiationTimeout: 60000,
// })

// connector.setConnectionConfig({
// signalingServerBaseUrl:
// 'wss://signaling-server-dev.rdx-works-main.extratools.works',
// })

// connector.setConnectionPassword(
// Buffer.from(
// 'c23a3446758d932eab6996a68680bbb3cce08a2b46fa28e1384f6bfaa0f562f8',
// 'hex',
// ),
// )

// connector.connect()
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
},
"types": "./dist/radix-connect-webrtc.d.ts",
"scripts": {
"dev": "npx tsx --watch ./examples/connector-client.ts",
"build": "rollup -c",
"prettier": "prettier --cache --check src",
"prettier:fix": "prettier --cache --write src",
Expand Down
28 changes: 15 additions & 13 deletions src/connector/webrtc/ice-candidate-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ export const IceCandidateClient = (input: {
peerConnection.addIceCandidate(iceCandidate),
errorIdentity,
)
.map(() => {
logger?.trace(
`🧊⬇️✅ addIceCandidateSuccess: ${iceCandidate.candidate}`,
)
return iceCandidate
})
.mapErr(() => {
logger?.trace(`🧊⬇️❌ addIceCandidateError: ${iceCandidate.candidate}`)
return iceCandidate
})

const subscriptions = new Subscription()

Expand All @@ -77,18 +87,10 @@ export const IceCandidateClient = (input: {
),
)

const haveLocalOffer$ = subjects.onSignalingStateChangeSubject.pipe(
filter((value) => value === 'have-local-offer'),
)

const haveRemoteOffer$ = subjects.onSignalingStateChangeSubject.pipe(
filter((value) => value === 'have-remote-offer'),
)
const waitForRemoteDescription$ = merge(
haveLocalOffer$,
haveRemoteOffer$,
subjects.onRemoteAnswerSubject,
)
const onRemoteDescriptionSuccess$ =
subjects.onRemoteDescriptionSuccessSubject.pipe(
filter((isSuccess) => isSuccess),
)

const onRemoteIceCandidate$ = merge(
subjects.remoteIceCandidatesSubject.pipe(
Expand All @@ -110,7 +112,7 @@ export const IceCandidateClient = (input: {
)

subscriptions.add(
waitForRemoteDescription$
onRemoteDescriptionSuccess$
.pipe(
mergeMap(() => onRemoteIceCandidate$),
concatMap(addIceCandidate),
Expand Down
55 changes: 51 additions & 4 deletions src/connector/webrtc/peer-connection-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
Offer,
} from '@radixdlt/radix-connect-schemas'
import { ResultAsync } from 'neverthrow'
import { mergeMap, Subscription, switchMap } from 'rxjs'
import { filter, mergeMap, Subscription, switchMap, tap } from 'rxjs'
import type { Logger } from 'tslog'
import { errorIdentity } from '../../utils/error-identity'
import type { WebRtcSubjectsType } from './subjects'
Expand All @@ -26,7 +26,12 @@ export const PeerConnectionClient = (input: {

const signalingClient = input.signalingClient
const onRemoteOffer$ = signalingClient.onOffer$
const onRemoteAnswer$ = signalingClient.onAnswer$

subscriptions.add(
signalingClient.onAnswer$
.pipe(tap((value) => subjects.remoteAnswerSubject.next(value)))
.subscribe(),
)

const onNegotiationNeeded = () => {
if (input.shouldCreateOffer)
Expand Down Expand Up @@ -54,20 +59,50 @@ export const PeerConnectionClient = (input: {
subjects.onSignalingStateChangeSubject.next(peerConnection.signalingState)
}

const onIceGatheringStateChange = () => {
logger?.debug(
`🕸🧊 onIceGatheringState: ${peerConnection.iceGatheringState}`,
)
}

const onIceConnectionStateChange = () => {
logger?.debug(
`🕸🧊 onIceConnectionStateChange: ${peerConnection.iceConnectionState}`,
)
}

peerConnection.onnegotiationneeded = onNegotiationNeeded
peerConnection.onsignalingstatechange = onSignalingStateChange
peerConnection.onicegatheringstatechange = onIceGatheringStateChange
peerConnection.oniceconnectionstatechange = onIceConnectionStateChange

const setLocalDescription = (description: RTCSessionDescriptionInit) =>
ResultAsync.fromPromise(
peerConnection.setLocalDescription(description),
errorIdentity,
).map(() => peerConnection.localDescription!)
)
.map(() => {
logger?.debug('🕸✅ localDescriptionSuccess')
subjects.onLocalDescriptionSuccessSubject.next(true)
return peerConnection.localDescription!
})
.mapErr((err) => {
logger?.debug('🕸❌ localDescriptionError')
return err
})

const setRemoteDescription = (description: RTCSessionDescriptionInit) =>
ResultAsync.fromPromise(
peerConnection.setRemoteDescription(description),
errorIdentity,
)
.map(() => {
logger?.debug('🕸✅ setRemoteDescriptionSuccess')
subjects.onRemoteDescriptionSuccessSubject.next(true)
})
.mapErr((error) => {
logger?.debug(`🕸❌ setRemoteDescriptionError: ${error}`)
})

const createAnswer = () =>
ResultAsync.fromPromise(peerConnection.createAnswer(), errorIdentity)
Expand All @@ -94,7 +129,19 @@ export const PeerConnectionClient = (input: {
)

subscriptions.add(
onRemoteAnswer$.pipe(mergeMap(setRemoteDescription)).subscribe(),
subjects.onLocalDescriptionSuccessSubject
.pipe(
filter((value) => value),
switchMap(() =>
subjects.remoteAnswerSubject.pipe(
filter(
(session): session is RTCSessionDescriptionInit => !!session,
),
mergeMap(setRemoteDescription),
),
),
)
.subscribe(),
)

return {
Expand Down
Loading
Loading