ntrnetwork
Version:
Transledger peer to peer wire communication
139 lines (113 loc) • 4.07 kB
JavaScript
const { EventEmitter } = require('events');
const secp256k1 = require('secp256k1');
const Buffer = require('safe-buffer').Buffer;
const { randomBytes } = require('crypto');
const ms = require('ms');
const { pk2id } = require('../util');
const KBucket = require('./kbucket');
const BanList = require('./ban-list');
const DPTServer = require('./server');
const debug = (process.env.DPT) ? process.env.DPT : require('../environment').debug.dpt;
class DPT extends EventEmitter {
constructor (privateKey, options) {
super()
this._privateKey = Buffer.from(privateKey)
this._id = pk2id(secp256k1.publicKeyCreate(this._privateKey, false))
this._banlist = new BanList()
this._kbucket = new KBucket(this._id)
this._kbucket.on('added', (peer) => this.emit('peer:added', peer))
this._kbucket.on('removed', (peer) => this.emit('peer:removed', peer))
this._kbucket.on('ping', (...args) => this._onKBucketPing(...args))
this._server = new DPTServer(this, this._privateKey, {
createSocket: options.createSocket,
timeout: options.timeout,
endpoint: options.endpoint
})
this._server.once('listening', () => this.emit('listening'))
this._server.once('close', () => this.emit('close'))
this._server.on('peers', (peers) => this._onServerPeers(peers))
this._server.on('error', (err) => this.emit('error', err))
const refreshInterval = options.refreshInterval || ms('60s')
this._refreshIntervalId = setInterval(() => this.refresh(), refreshInterval)
}
get server() {
return this._server;
}
bind (...args) {
this._server.bind(...args)
}
destroy (...args) {
clearInterval(this._refreshIntervalId)
this._server.destroy(...args)
}
_onKBucketPing (oldPeers, newPeer) {
if (this._banlist.has(newPeer)) return
let count = 0
let err = null
for (let peer of oldPeers) {
this._server.ping(peer)
.catch((_err) => {
this._banlist.add(peer, ms('5m'))
this._kbucket.remove(peer)
err = err || _err
})
.then(() => {
if (++count < oldPeers.length) return
if (err === null) this._banlist.add(newPeer, ms('5m'))
else this._kbucket.add(newPeer)
})
}
}
_onServerPeers (peers) {
for (let peer of peers) this.addPeer(peer).catch(() => {})
}
async bootstrap (peer) {
debug ? console.log(`${Date().toString().substring(0, 24)} dpt.index: bootstrap with peer ${peer.address}:${peer.udpPort}`) : null;
peer = await this.addPeer(peer)
this._server.findneighbours(peer, this._id)
}
async addPeer (obj) {
// Check if the new peer is banned.
if (this._banlist.has(obj))
throw new Error('Peer is banned');
else
debug ? console.log(`${Date().toString().substring(0, 24)} dpt.index: attempt adding peer ${obj.address}:${obj.udpPort}`) : null;
// check k-bucket first
const peer = this._kbucket.get(obj)
if (peer !== null) return peer
// check that peer is alive
try {
const peer = await this._server.ping(obj);
if (obj.channelID)
peer.channelID = obj.channelID;
this.emit('peer:new', peer)
this._kbucket.add(peer)
return peer
} catch (err) {
this._banlist.add(obj, ms('5m'))
throw err
}
}
getPeer (obj) {
return this._kbucket.get(obj)
}
getPeers () {
return this._kbucket.getAll()
}
getClosestPeers (id) {
return this._kbucket.closest(id)
}
removePeer (obj) {
this._kbucket.remove(obj)
}
banPeer (obj, maxAge) {
this._banlist.add(obj, maxAge)
this._kbucket.remove(obj)
}
refresh () {
const peers = this.getPeers()
debug ? console.log(`${Date().toString().substring(0, 24)} index/dpt.refresh ***(${peers.length} peers in table)***`) : null;
for (let peer of peers) this._server.findneighbours(peer, randomBytes(64))
}
}
module.exports = DPT