ntrnetwork
Version:
Transledger peer to peer wire communication
150 lines (128 loc) • 4.97 kB
JavaScript
/* -----------------------------------------------------------------------------
interblockchain wire protocol
Description:
This module implements a peer to peer protocol used to send messages to
interblockchain nodes.
This layer process the following protocol messages
- STATUS
- TX
- AUDIT
Author:
Didier PH Martin 2018-06-28
Copyright:
(c) Interblockchain 2018.
--------------------------------------------------------------------------------*/
const { EventEmitter } = require('events')
const rlp = require('rlp-encoding')
const ms = require('ms')
const Buffer = require('safe-buffer').Buffer
const { int2buffer, buffer2int, assertEq } = require('../util')
const Peer = require('../rlpx/peer')
const debug = (process.env.INTERBLOCKCHAIN) ? process.env.INTERBLOCKCHAIN : require('../environment').debug.interblockchain;
// WARNING:
// When a new message code is added,
// the following elements should be modified
// - the static ntr property
// - the _handleMessage function
// see below...
//
const MESSAGE_CODES = {
STATUS: 0x00,
TX: 0x02,
AUDIT: 0x03
}
class NTR extends EventEmitter {
constructor (version, peer, send) {
super()
this._version = version
this._peer = peer
this._send = send
this._status = null
this._peerStatus = null
this._statusTimeoutId = setTimeout(() => {
this._peer.disconnect(Peer.DISCONNECT_REASONS.TIMEOUT)
}, ms('60s'))
}
// name: protocol name
// version: should be an integer (ex: 1 and not 0.2)
// length: number of message codes. See MESSAGE_CODES
// It should be the Nbr of codes + 1.
// constructor: name of this class
static get ntr() { return {
name: 'NTR',
version: 1,
length: 4,
constructor: NTR }
}
static get MESSAGE_CODES() {return MESSAGE_CODES}
// WARNING:
// when a new code is added this function should be updated
// to process the new code
_handleMessage (code, data) {
const payload = rlp.decode(data)
if (code !== MESSAGE_CODES.STATUS) {
debug ? console.log(`${Date().toString().substring(0, 24)} NTR._handleMessage Received ${this.getMsgPrefix(code)} message from ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}`) : null;
}
switch (code) {
case MESSAGE_CODES.STATUS:
assertEq(this._peerStatus, null, 'Uncontrolled status message')
this._peerStatus = payload
debug ? console.log(`${Date().toString().substring(0, 24)} NTR._handleMessage Received ${this.getMsgPrefix(code)} message from ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}: : ${this._getStatusString(this._peerStatus)}`) : null;
this._handleStatus()
break ;
case MESSAGE_CODES.TX:
case MESSAGE_CODES.AUDIT:
clearTimeout(this._statusTimeoutId)
break;
default:
return
}
this.emit('message',code, payload)
}
_handleStatus () {
if (this._status === null || this._peerStatus === null) return
clearTimeout(this._statusTimeoutId)
assertEq(this._status[0], this._peerStatus[0], 'Protocol version mismatch')
assertEq(this._status[1], this._peerStatus[1], 'NetworkId mismatch')
this.emit('status', {
protocolVersion:buffer2int(this._peerStatus[0]),
networkId: buffer2int(this._peerStatus[1]),
nodeID: (this._peerStatus[2]) ? this._peerStatus[2].toString(): "interblockchain"
})
}
getVersion () {
return this._version
}
_getStatusString (status) {
var sStr = `[V:${buffer2int(status[0])}, NID:${buffer2int(status[1])}]`
return sStr
}
sendStatus (status) {
if (this._status !== null) return
this._status = [
int2buffer(this._version),
int2buffer(status.networkId),
Buffer.from(status.nodeID)
]
debug ? console.log(`${Date().toString().substring(0, 24)} NTR.sendStatus message to ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort} (eth${this._version}): ${this._getStatusString(this._status)}`) : null;
this._send(MESSAGE_CODES.STATUS, rlp.encode(this._status))
this._handleStatus()
}
sendMessage (code, payload) {
debug ? console.log(`${Date().toString().substring(0, 24)} NTR.SendMessage ${this.getMsgPrefix(code)} message to ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}`) : null;
switch (code) {
case MESSAGE_CODES.STATUS:
throw new Error('Please send status message through .sendStatus')
case MESSAGE_CODES.TX:
case MESSAGE_CODES.AUDIT:
break;
default:
throw new Error(`Unknown code ${code}`)
}
this._send(code, rlp.encode(payload))
}
getMsgPrefix (msgCode) {
return Object.keys(MESSAGE_CODES).find(key => MESSAGE_CODES[key] === msgCode)
}
}
module.exports = NTR