UNPKG

zeronet-common

Version:
196 lines (186 loc) 5.1 kB
'use strict' const EventEmitter = require('events').EventEmitter const ip2multi = require('../network/ip2multi') const multi2ip = ip2multi.reverse4 const PeerInfo = require('peer-info') const Id = require('peer-id') const multiaddr = require('multiaddr') const once = require('once') const debug = require('debug') const log = process.env.INTENSE_DEBUG ? debug('zeronet:peer') : () => {} const pull = require('pull-stream') const crypto = require('crypto') const sha5 = text => crypto.createHash('sha512').update(text).digest('hex') class Hashfield { constructor () { // eslint-disable-line // TODO: implement } } class ZiteInfo { constructor (addr) { this.addr = addr this.hashfield = new Hashfield() } toJSON () { return { addr: this.addr } } } class Peer extends EventEmitter { constructor (addrs, id) { super() const oe = this.emit.bind(this) this.emit = function () { const a = [...arguments] oe.apply(null, a) a.unshift('emit') oe.apply(null, a) } if (Id.isPeerId(id)) { this.id = id.toB58String() this._id = id } else { this._id = new Id(Buffer.from(id)) this.id = this._id.toB58String() } if (PeerInfo.isPeerInfo(addrs)) { this.pi = addrs } else { this.pi = new PeerInfo(this._id) addrs.map(addr => multiaddr.isMultiaddr(addr) ? addr : multiaddr(addr)).forEach(addr => this.pi.multiaddrs.add(addr)) } this.addrs = this.pi.multiaddrs.toArray().map(a => a.toString()) this.multiaddr = this.addrs[0] log('created peer %s with address(es) %s', this.id, this.addrs.join(', ')) this.zites = {} this.score = 0 this.lfailedd = 0 // last failed dial time } seed (zite) { if (!this.zites[zite]) { log('peer %s (%s) now seeds %s', this.id, this.addrs.join(', '), zite) this.zites[zite] = new ZiteInfo(zite) this.emit('seed', zite) this.emit('seed.' + zite) } } isSeeding (zite) { return Boolean(this.zites[zite]) } dial (cb) { if (this.score <= -100) return cb(new Error('Score too low')) if (this.isOnline) return cb() const offline = once(() => { this.isOnline = false this.emit('offline') }) const ncb = once((err, conn) => { if (err) { this.score -= 10 this.lfailedd = new Date().getTime() } else { this.score += 5 this.isOnline = true this.emit('online') if (this.type === 'zero') { if (conn) conn.once('end', offline) else offline() // Peer was upgraded. This one is now offline } else if (this.type === 'lp2p') { pull( conn, pull.onEnd(offline) ) } } cb(err) }) if (this.type === 'zero') { this.swarm.dial(this.dialable, ncb) } else if (this.type === 'lp2p') { this.swarm.dial(this.dialable, '/isOnline/1.0.0', ncb) } setTimeout(ncb, 10 * 1000, new Error('Timeout')) } cmd (cmd, data, cb) { this.dial(err => { if (err) return cb(err) const ncb = once((err, res) => { if (err) { this.score -= 5 this.isOnline = true } else { this.score += 5 } cb(err, res) }) this.swarm.dial(this.dialable, cmd, data, ncb) setTimeout(ncb, 10 * 1000, new Error('Timeout')) }) } toJSON () { return { addrs: this.addrs, id: this.id, ip: this.ip, score: this.score, zites: Object.keys(this.zites).map(zite => this.zites[zite].toJSON()) } } } class ZeroPeer extends Peer { constructor (addr) { super([addr], sha5(sha5(addr)).substr(0, 20)) this.type = 'zero' this.ip = multi2ip(addr) this.dialable = multiaddr(addr) } discoverZite (zite, cb) { if (this.isSeeding(zite)) return cb(true) // eslint-disable-line standard/no-callback-literal cb(false) // eslint-disable-line standard/no-callback-literal } } class Lp2pPeer extends Peer { constructor (peer) { super(peer, peer.id) this.type = 'lp2p' this.dialable = peer } discoverZite (zite, cb) { if (this.isSeeding(zite)) return cb(true) // eslint-disable-line standard/no-callback-literal this.cmd('hasZite', { zite }, (err, res) => { log(err, res) if (err) return cb(false) // eslint-disable-line standard/no-callback-literal else { if (res.has) this.seed(zite) return cb(res.has) } }) } } function fromJSON (data) { if (!data.id) return let peer if (data.ip) { peer = new ZeroPeer(data.addrs[0]) } else { const id = Id.createFromB58String(data.id) const pi = new PeerInfo(id) data.addrs.forEach(a => pi.multiaddrs.addSafe(a)) peer = new Lp2pPeer(pi) } data.zites.forEach(zite_ => { let zite = new ZiteInfo(zite_.addr) // TODO: hashfield peer.zites[zite.addr] = zite }) log('peer %s seeds %s', peer.id, data.zites.map(z => z.addr).join(', ')) return peer } module.exports = { ZeroPeer, Lp2pPeer, fromJSON }