UNPKG

libp2p-exchange-rendezvous

Version:

Data Exchange component that uses other peers to exchange requests/responses

220 lines (179 loc) 5.13 kB
'use strict' /* eslint-disable max-nested-callbacks */ const debug = require('debug') const log = debug('libp2p:exchange:rendezvous:rpc') const Pushable = require('pull-pushable') const pull = require('pull-stream') const {Type, ErrorType, ETABLE} = require('./proto.js') const Id = require('peer-id') const once = require('once') const wrap = (cb) => { cb = once(cb) setTimeout(() => cb(new Error('Timeout')), 10 * 1000) return cb } module.exports = (myId, requestHandler) => { let online = true let cbs = {} let id = 1 const source = Pushable() const sink = pull.drain(data => { log('got %s %s', data.type, data.id) switch (data.type) { case Type.ID_LOOKUP: { let cb = cbs[data.id] if (cb && cb.requestedId) { delete cbs[data.id] log('got id lookup response %s', data.id) if (data.error) { return cb(new Error(ETABLE[data.error] || 'N/A')) } Id.createFromProtobuf(data.remote, (err, id) => { if (err) { return cb(err) } if (id.toB58String() !== cb.requestedId) { return cb(new Error('Id is not matching!')) } return cb(null, id) }) } break } case Type.REQUEST: { let cb = (err, res) => { let out = { type: Type.RESPONSE, id: data.id } if (err) { out.error = err } else { Object.assign(out, res) } source.push(out) } log('got request %s', data.ns) Id.createFromProtobuf(data.remote, (err, remoteId) => { if (err) { log(err) return cb(ErrorType.E_NACK) } remoteId.pubKey.verify(data.data, data.signature, (err, ok) => { if (err || !ok) { log(err || 'Signature check failed') return cb(ErrorType.E_NACK) } myId.privKey.decrypt(data.data, (err, request) => { if (err) { log(err) return cb(ErrorType.E_NACK) } requestHandler(data.ns, remoteId, request, (err, res) => { if (err) { log(err) return cb(ErrorType.E_NACK) } if (res.nack) { return cb(ErrorType.E_NACK) } remoteId.pubKey.encrypt(res.result, (err, result) => { if (err) { log(err) return cb(ErrorType.E_OTHER) } myId.privKey.sign(result, (err, signature) => { if (err) { log(err) return cb(ErrorType.E_OTHER) } cb(null, {data: result, signature}) }) }) }) }) }) }) break } case Type.RESPONSE: { let cb = cbs[data.id] if (cb && cb.remoteId) { delete cbs[data.id] if (data.error) { return cb(new Error(ETABLE[data.error] || 'N/A')) } cb.remoteId.pubKey.verify(data.data, data.signature, (err, ok) => { if (err || !ok) { return cb(err || new Error('Signature check failed')) } myId.privKey.decrypt(data.data, (err, result) => { if (err) { return cb(err) } return cb(null, result) }) }) } break } default: { log('rpc got unknown type %s', data.type) } } }, e => { source.end(e) online = false }) source.push({ // send this fake ID_LOOKUP as first packet so the server has our pubKey type: Type.ID_LOOKUP, id: 0, remote: myId.marshal(true) }) return { source, sink, methods: { online: () => online, lookup: (b58, cb) => { if (!online) { return cb(new Error('Not online!')) } let rid = id++ * 2 cbs[rid] = wrap(cb) cbs[rid].requestedId = b58 source.push({ type: Type.ID_LOOKUP, id: rid, remote: Buffer.from(b58) }) }, request: (remoteId, ns, data, cb) => { if (!online) { return cb(new Error('Not online!')) } remoteId.pubKey.encrypt(data, (err, data) => { if (err) { return cb(err) } myId.privKey.sign(data, (err, signature) => { if (err) { return cb(err) } let rid = id++ * 2 cbs[rid] = wrap(cb) cbs[rid].remoteId = remoteId source.push({ type: Type.REQUEST, id: rid, ns, data, signature, remote: Buffer.from(remoteId.toB58String()) }) }) }) } } } }