UNPKG

ntrnetwork

Version:

Transledger peer to peer wire communication

150 lines (128 loc) 4.97 kB
/* ----------------------------------------------------------------------------- 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