Skip to content

Commit e0e0e14

Browse files
author
nullagent
committed
initial p2p match maker
1 parent 110e5c5 commit e0e0e14

17 files changed

Lines changed: 988 additions & 129 deletions

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@
5757
"build-test": "node ./examples/party/example-build.js",
5858
"watch-test": "DEBUG=* nodemon --ignore service test/test-service-compile.js",
5959
"build-docs": "./scripts/build-docs.sh",
60-
"generate-docs": "npx jsdoc --configure jsdoc.json --verbose"
60+
"build-venue": "node ./src/venue/build.js",
61+
"generate-docs": "npx jsdoc --configure jsdoc.json --verbose",
62+
"mkssl": "openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem"
6163
},
6264
"dependencies": {
6365
"@dataparty/bouncer-db": "1.0.1",
@@ -79,6 +81,7 @@
7981
"dom-storage": "^2.1.0",
8082
"eventemitter3": "^4.0.7",
8183
"express": "^4.17.1",
84+
"express-ipfilter": "^1.3.2",
8285
"express-list-routes": "^1.1.9",
8386
"git-repo-info": "^2.1.1",
8487
"joi": "^17.9.1",

src/party/iparty.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,15 @@ class IParty {
168168
*/
169169
get identity(){
170170
if (!this.hasIdentity()){ return undefined }
171-
return dataparty_crypto.Identity.fromString(this._identity.toString())
171+
return dataparty_crypto.Identity.fromBSON(this._identity.toBSON())
172+
}
173+
174+
async setIdentity(newIdentity){
175+
this._identity = newIdentity
176+
177+
const identityBson = this._identity.toBSON(true)
178+
const identityBase64 = dataparty_crypto.Routines.Utils.base64.encode(identityBson)
179+
await this.config.write(path, identityBase64)
172180
}
173181

174182
/**
@@ -251,8 +259,14 @@ class IParty {
251259

252260
await this.resetIdentity()
253261
} else {
254-
debug('loaded identity')
255-
this._identity = dataparty_crypto.Identity.fromString(JSON.stringify(cfgIdenStr))
262+
try{
263+
const identityBson = dataparty_crypto.Routines.Utils.base64.decode(cfgIdenStr)
264+
this._identity = dataparty_crypto.Identity.fromBSON(identityBson)
265+
debug('loaded identity (bson)')
266+
} catch (err){
267+
this._identity = dataparty_crypto.Identity.fromString( JSON.stringify(cfgIdenStr) )
268+
debug('loaded identity (json)')
269+
}
256270
}
257271
}
258272

@@ -266,7 +280,10 @@ class IParty {
266280

267281
this._identity = new dataparty_crypto.Identity({id: 'primary'})
268282
await this._identity.initialize()
269-
await this.config.write(path, this._identity.toJSON(true))
283+
284+
const identityBson = this._identity.toBSON(true)
285+
const identityBase64 = dataparty_crypto.Routines.Utils.base64.encode(identityBson)
286+
await this.config.write(path, identityBase64)
270287

271288
await this.loadIdentity()
272289
}

src/party/peer/p2p-match-maker.js

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
const EventEmitter = require('eventemitter3')
2+
3+
const dataparty_crypto = require('@dataparty/crypto')
4+
const LokiParty = require('../local/loki-party')
5+
const PeerParty = require('../local/peer-party')
6+
const MemoryConfig = require('../../config/memory')
7+
const WebsocketComms = require('../../comms/websocket-comms')
8+
9+
const PeerInvite = require('./peer-invite')
10+
11+
class MatchMakerClient extends EventEmitter {
12+
constructor(identity, contacts, urlOrParty = 'https://postquantum.one/api/', wsUrlOrParty = 'https://postquantum.one/api/ws'){
13+
14+
super()
15+
16+
this.contacts = contacts
17+
this.identity = identity
18+
this.wsParty = null
19+
this.restParty = null
20+
21+
if(typeof urlOrParty == 'string'){
22+
this.restUrl = urlOrParty
23+
this.restParty = null
24+
} else {
25+
this.restParty = urlOrParty
26+
}
27+
28+
if(typeof wsUrlOrParty == 'string'){
29+
this.wsUrl = wsUrlOrParty
30+
this.wsParty = null
31+
} else {
32+
this.wsParty = wsUrlOrParty
33+
}
34+
35+
this.invitesTx = null
36+
this.invitesRx = null
37+
38+
this.pendingInvites = {
39+
tx: {},
40+
rx: {}
41+
}
42+
//this.restParty =
43+
}
44+
45+
46+
47+
async start(){
48+
/*
49+
*
50+
*/
51+
52+
if(!this.restParty){
53+
let config = new MemoryConfig({
54+
basePath:'match-maker-client',
55+
cloud: {
56+
uri: this.restUrl
57+
}
58+
})
59+
60+
this.restParty = new LokiParty({
61+
path: 'match-maker-client',
62+
dbAdapter: new LokiParty.Loki.LokiMemoryAdapter(),
63+
config
64+
})
65+
66+
if(this.identity){ await this.restParty.setIdentity(this.identity) }
67+
68+
debug('starting restParty')
69+
await this.restParty.start()
70+
71+
await this.announcePublicKeys()
72+
}
73+
74+
if(!this.wsParty){
75+
this.wsParty = new PeerParty({
76+
comms: new WebsocketComms({
77+
uri: this.wsUrl,
78+
discoverRemoteIdentity: false,
79+
remoteIdentity: await this.restParty.comms.getServiceIdentity(),
80+
session: Math.random().toString(36).slice(2)
81+
}),
82+
config: this.restParty.config
83+
})
84+
85+
//if(this.identity){ await this.wsParty.setIdentity(this.identity) }
86+
87+
await this.wsParty.start()
88+
89+
debug('starting wsParty')
90+
await this.wsParty.start()
91+
debug('waiting for websocket authorization')
92+
await this.wsParty.comms.authorized()
93+
94+
this.invitesRx = this.wsParty.ROSLIB.Topic({
95+
ros : this.wsParty.comms.ros,
96+
name : '/invites/' + encodeURIComponent(this.restParty.identity.key.hash) + '/rx',
97+
messageType: 'Object'
98+
})
99+
100+
this.invitesRx.subscribe( this.handleInviteRxMsg.bind(this) )
101+
102+
103+
this.invitesTx = this.wsParty.ROSLIB.Topic({
104+
ros : this.wsParty.comms.ros,
105+
name : '/invites/' + encodeURIComponent(this.restParty.identity.key.hash) + '/tx',
106+
messageType: 'Object'
107+
})
108+
109+
this.invitesTx.subscribe( this.handleInviteTxMsg.bind(this) )
110+
}
111+
112+
}
113+
114+
async handleInviteRxMsg( msg ){
115+
debug('handleInviteRxMsg', this.invitesRx.name, msg)
116+
117+
const inviteId = msg.invite.$meta.id
118+
119+
if(!this.pendingInvites.rx[inviteId] && msg.invite.state == 'invited'){
120+
121+
const from = await this.lookupPublicKey(msg.invite.fromHash)
122+
const to = await this.lookupPublicKey(msg.invite.toHash)
123+
124+
let invite = new PeerInvite(msg.invite, to, this, from)
125+
126+
this.pendingInvites.rx[inviteId] = invite
127+
128+
this.emit('invited', invite)
129+
130+
} else if(this.pendingInvites.rx[inviteId]) {
131+
132+
let invite = this.pendingInvites.rx[inviteId]
133+
invite.onInviteMsg(msg.invite)
134+
}
135+
}
136+
137+
async handleInviteTxMsg( msg ){
138+
debug('handleInviteTxMsg', this.invitesTx.name, msg)
139+
140+
const inviteId = msg.invite.$meta.id
141+
let pending = this.pendingInvites.tx[inviteId]
142+
143+
if( pending &&
144+
//msg.invite.state == 'accepted' &&
145+
pending.$meta.id == msg.invite.$meta.id
146+
){
147+
148+
pending.onInviteMsg(msg.invite)
149+
150+
}
151+
}
152+
153+
async announcePublicKeys(){
154+
const announceData = {
155+
type: this.restParty.identity.key.type,
156+
hash: this.restParty.identity.key.hash,
157+
public: this.restParty.identity.key.public
158+
}
159+
160+
debug('announcePublicKeys', announceData)
161+
162+
const announceResult = await this.restParty.comms.call('key/announce', announceData, {
163+
expectClearTextReply: false,
164+
sendClearTextRequest: false,
165+
useSessions: false
166+
})
167+
}
168+
169+
170+
async lookupPublicKey(hash){
171+
debug('lookupPublicKey - hash:', hash)
172+
173+
if(hash == this.restParty.identity.key.hash){
174+
return this.restParty.identity
175+
}
176+
177+
if(this.contacts){
178+
return await this.contacts.lookupPublicKey(hash)
179+
}
180+
181+
const lookupData = { hash }
182+
183+
const lookupResult = await restParty.comms.call('key/lookup', lookupData, {
184+
expectClearTextReply: false,
185+
sendClearTextRequest: false,
186+
useSessions: false
187+
})
188+
189+
if(!lookupResult.done){
190+
return null
191+
}
192+
193+
debug('lookup result -', lookupResult)
194+
195+
const identity = new dataparty_crypto.Identity({
196+
key: lookupResult.public_key
197+
})
198+
199+
return identity
200+
}
201+
202+
async createInvite(toHashOrIdentity, {type, service, role, session}, info){
203+
204+
debug('createInvite')
205+
206+
let toIdentity = null
207+
if(typeof toHashOrIdentity == 'string'){
208+
const toIdentity = await lookupPublicKey(userCallHashInput.value)
209+
thisotherIdentity = toIdentity
210+
} else {
211+
toIdentity = toHashOrIdentity
212+
}
213+
214+
215+
216+
debug('toIdentity', toIdentity)
217+
218+
const invitePayload = {
219+
type: type ? type : 'webrtc',
220+
service: service ? service : '@dataparty/video-chat',
221+
role: role ? role : 'client',
222+
timestamp: (new Date()).getTime(),
223+
from: this.wsParty.identity.key.hash,
224+
to: toIdentity.key.hash,
225+
session: session ? session : Math.random().toString(36).slice(2),
226+
info: info ? info : {
227+
roomId: '',
228+
action: 'call',
229+
}
230+
}
231+
232+
const secureInvite = await this.wsParty.privateIdentity.encrypt(invitePayload, toIdentity)
233+
234+
debug('secure-invite', secureInvite)
235+
236+
const invitePostData = {
237+
to: toIdentity.key.hash,
238+
from: party.identity.key.hash,
239+
payload: JSON.stringify(secureInvite.toJSON())
240+
}
241+
242+
const inviteResult = await this.restParty.comms.call('invite/create', invitePostData, {
243+
expectClearTextReply: false,
244+
sendClearTextRequest: false,
245+
useSessions: false
246+
})
247+
248+
const inviteDoc = inviteResult.invite
249+
250+
if(!inviteDoc){ return }
251+
252+
let invite = new PeerInvite(inviteResult.invite, toIdentity, this, this.restParty.identity)
253+
254+
invite.payload = invitePayload
255+
256+
this.pendingInvites.tx[inviteDoc.$meta.id] = invite
257+
258+
return invite
259+
}
260+
261+
async lookupInvites({createdAfter, type='to', id, actorHash }){
262+
let actor = this.wsParty.identity.key.hash
263+
264+
const lookup = {
265+
invite:id, actor:actorHash ? actorHash : this.restParty.identity.key.hash,
266+
createdAfter,
267+
type: !type ? 'to' : type
268+
}
269+
270+
const lookupResult = await this.restParty.comms.call('invite/lookup', lookup, {
271+
expectClearTextReply: false,
272+
sendClearTextRequest: false,
273+
useSessions: false
274+
})
275+
276+
if(!lookupResult.done){
277+
return null
278+
}
279+
280+
return lookupResult.invites
281+
}
282+
283+
async getPeerInvitesFromInviteDocs(invites){
284+
285+
let peerInvites = []
286+
287+
for(let i=0; i < invites.length; i++){
288+
289+
const invite = invites[i]
290+
let to = await this.lookupPublicKey( invite.toHash )
291+
let from = await this.lookupPublicKey( invite.fromHash )
292+
293+
let peerInvite = new PeerInvite( invites[i], to, this, from)
294+
295+
peerInvites.push(peerInvite)
296+
}
297+
298+
return peerInvites
299+
}
300+
301+
async setInviteState(invite, newState){
302+
303+
debug('setInviteState')
304+
let actor = this.restParty.identity.key.hash
305+
306+
const inviteState = {
307+
invite: invite.inviteDoc.$meta.id,
308+
actor,
309+
state: newState
310+
}
311+
312+
const inviteStateResult = await this.restParty.comms.call('invite/set-state', inviteState, {
313+
expectClearTextReply: false,
314+
sendClearTextRequest: false,
315+
useSessions: false
316+
})
317+
318+
console.log('setInviteState result', inviteStateResult)
319+
320+
if(!inviteStateResult.done){
321+
return null
322+
}
323+
324+
return inviteStateResult.invite
325+
}
326+
}
327+
328+
module.exports = MatchMakerClient

0 commit comments

Comments
 (0)