/ src / les / index.js
index.js
  1  const { EventEmitter } = require('events')
  2  const rlp = require('rlp-encoding')
  3  const ms = require('ms')
  4  const { int2buffer, buffer2int, assertEq } = require('../util')
  5  const Peer = require('../rlpx/peer')
  6  
  7  const createDebugLogger = require('debug')
  8  const debug = createDebugLogger('devp2p:les')
  9  
 10  const MESSAGE_CODES = {
 11    // LES/1
 12    STATUS: 0x00,
 13    ANNOUNCE: 0x01,
 14    GET_BLOCK_HEADERS: 0x02,
 15    BLOCK_HEADERS: 0x03,
 16    GET_BLOCK_BODIES: 0x04,
 17    BLOCK_BODIES: 0x05,
 18    GET_RECEIPTS: 0x06,
 19    RECEIPTS: 0x07,
 20    GET_PROOFS: 0x08,
 21    PROOFS: 0x09,
 22    GET_CONTRACT_CODES: 0x0a,
 23    CONTRACT_CODES: 0x0b,
 24    GET_HEADER_PROOFS: 0x0d,
 25    HEADER_PROOFS: 0x0e,
 26    SEND_TX: 0x0c,
 27  
 28    // LES/2
 29    GET_PROOFS_V2: 0x0f,
 30    PROOFS_V2: 0x10,
 31    GET_HELPER_TRIE_PROOFS: 0x11,
 32    HELPER_TRIE_PROOFS: 0x12,
 33    SEND_TX_V2: 0x13,
 34    GET_TX_STATUS: 0x14,
 35    TX_STATUS: 0x15
 36  }
 37  
 38  const DEFAULT_ACCOUNCE_TYPE = 1
 39  
 40  class LES extends EventEmitter {
 41    constructor (version, peer, send) {
 42      super()
 43  
 44      this._version = version
 45      this._peer = peer
 46      this._send = send
 47  
 48      this._status = null
 49      this._peerStatus = null
 50      this._statusTimeoutId = setTimeout(() => {
 51        this._peer.disconnect(Peer.DISCONNECT_REASONS.TIMEOUT)
 52      }, ms('5s'))
 53    }
 54  
 55    static les2 = { name: 'les', version: 2, length: 21, constructor: LES }
 56  
 57    static MESSAGE_CODES = MESSAGE_CODES
 58  
 59    _handleMessage (code, data) {
 60      const payload = rlp.decode(data)
 61      if (code !== MESSAGE_CODES.STATUS) {
 62        debug(`Received ${this.getMsgPrefix(code)} message from ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}: ${data.toString('hex')}`)
 63      }
 64      switch (code) {
 65        case MESSAGE_CODES.STATUS:
 66          assertEq(this._peerStatus, null, 'Uncontrolled status message')
 67          let statusArray = {}
 68          payload.forEach(function (value) {
 69            statusArray[value[0].toString()] = value[1]
 70          })
 71          this._peerStatus = statusArray
 72          debug(`Received ${this.getMsgPrefix(code)} message from ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}: : ${this._getStatusString(this._peerStatus)}`)
 73          this._handleStatus()
 74          break
 75  
 76        case MESSAGE_CODES.ANNOUNCE:
 77        case MESSAGE_CODES.GET_BLOCK_HEADERS:
 78        case MESSAGE_CODES.BLOCK_HEADERS:
 79        case MESSAGE_CODES.GET_BLOCK_BODIES:
 80        case MESSAGE_CODES.BLOCK_BODIES:
 81        case MESSAGE_CODES.GET_RECEIPTS:
 82        case MESSAGE_CODES.RECEIPTS:
 83        case MESSAGE_CODES.GET_PROOFS:
 84        case MESSAGE_CODES.PROOFS:
 85        case MESSAGE_CODES.GET_CONTRACT_CODES:
 86        case MESSAGE_CODES.CONTRACT_CODES:
 87        case MESSAGE_CODES.GET_HEADER_PROOFS:
 88        case MESSAGE_CODES.HEADER_PROOFS:
 89        case MESSAGE_CODES.SEND_TX:
 90        case MESSAGE_CODES.GET_PROOFS_V2:
 91        case MESSAGE_CODES.PROOFS_V2:
 92        case MESSAGE_CODES.GET_HELPER_TRIE_PROOFS:
 93        case MESSAGE_CODES.HELPER_TRIE_PROOFS:
 94        case MESSAGE_CODES.SEND_TX_V2:
 95        case MESSAGE_CODES.GET_TX_STATUS:
 96        case MESSAGE_CODES.TX_STATUS:
 97          if (this._version >= LES.les2.version) break
 98          return
 99  
100        default:
101          return
102      }
103  
104      this.emit('message', code, payload)
105    }
106  
107    _handleStatus () {
108      if (this._status === null || this._peerStatus === null) return
109      clearTimeout(this._statusTimeoutId)
110      assertEq(this._status['protocolVersion'], this._peerStatus['protocolVersion'], 'Protocol version mismatch')
111      assertEq(this._status['networkId'], this._peerStatus['networkId'], 'NetworkId mismatch')
112      assertEq(this._status['genesisHash'], this._peerStatus['genesisHash'], 'Genesis block mismatch')
113  
114      this.emit('status', this._peerStatus)
115    }
116  
117    getVersion () {
118      return this._version
119    }
120  
121    _getStatusString (status) {
122      var sStr = `[V:${buffer2int(status['protocolVersion'])}, `
123      sStr += `NID:${buffer2int(status['networkId'])}, HTD:${buffer2int(status['headTd'])}, `
124      sStr += `HeadH:${status['headHash'].toString('hex')}, HeadN:${buffer2int(status['headNum'])}, `
125      sStr += `GenH:${status['genesisHash'].toString('hex')}`
126      if (status['serveHeaders']) sStr += `, serveHeaders active`
127      if (status['serveChainSince']) sStr += `, ServeCS: ${buffer2int(status['serveChainSince'])}`
128      if (status['serveStateSince']) sStr += `, ServeSS: ${buffer2int(status['serveStateSince'])}`
129      if (status['txRelax']) sStr += `, txRelay active`
130      if (status['flowControl/BL']) sStr += `, flowControl/BL set`
131      if (status['flowControl/MRR']) sStr += `, flowControl/MRR set`
132      if (status['flowControl/MRC']) sStr += `, flowControl/MRC set`
133      sStr += `]`
134      return sStr
135    }
136  
137    sendStatus (status) {
138      if (this._status !== null) return
139  
140      if (!status.announceType) {
141        status['announceType'] = DEFAULT_ACCOUNCE_TYPE
142      }
143      status['announceType'] = int2buffer(status['announceType'])
144      status['protocolVersion'] = int2buffer(this._version)
145      status['networkId'] = int2buffer(status['networkId'])
146  
147      this._status = status
148  
149      let statusList = []
150      Object.keys(status).forEach((key) => {
151        statusList.push([key, status[key]])
152      })
153  
154      debug(`Send STATUS message to ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort} (les${this._version}): ${this._getStatusString(this._status)}`)
155      this._send(MESSAGE_CODES.STATUS, rlp.encode(statusList))
156      this._handleStatus()
157    }
158  
159    sendMessage (code, reqId, payload) {
160      debug(`Send ${this.getMsgPrefix(code)} message to ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}: ${rlp.encode(payload).toString('hex')}`)
161      switch (code) {
162        case MESSAGE_CODES.STATUS:
163          throw new Error('Please send status message through .sendStatus')
164  
165        case MESSAGE_CODES.ANNOUNCE: // LES/1
166        case MESSAGE_CODES.GET_BLOCK_HEADERS:
167        case MESSAGE_CODES.BLOCK_HEADERS:
168        case MESSAGE_CODES.GET_BLOCK_BODIES:
169        case MESSAGE_CODES.BLOCK_BODIES:
170        case MESSAGE_CODES.GET_RECEIPTS:
171        case MESSAGE_CODES.RECEIPTS:
172        case MESSAGE_CODES.GET_PROOFS:
173        case MESSAGE_CODES.PROOFS:
174        case MESSAGE_CODES.GET_CONTRACT_CODES:
175        case MESSAGE_CODES.CONTRACT_CODES:
176        case MESSAGE_CODES.GET_HEADER_PROOFS:
177        case MESSAGE_CODES.HEADER_PROOFS:
178        case MESSAGE_CODES.SEND_TX:
179        case MESSAGE_CODES.GET_PROOFS_V2: // LES/2
180        case MESSAGE_CODES.PROOFS_V2:
181        case MESSAGE_CODES.GET_HELPER_TRIE_PROOFS:
182        case MESSAGE_CODES.HELPER_TRIE_PROOFS:
183        case MESSAGE_CODES.SEND_TX_V2:
184        case MESSAGE_CODES.GET_TX_STATUS:
185        case MESSAGE_CODES.TX_STATUS:
186          if (this._version >= LES.les2.version) break
187          throw new Error(`Code ${code} not allowed with version ${this._version}`)
188  
189        default:
190          throw new Error(`Unknown code ${code}`)
191      }
192  
193      this._send(code, rlp.encode([reqId, payload]))
194    }
195  
196    getMsgPrefix (msgCode) {
197      return Object.keys(MESSAGE_CODES).find(key => MESSAGE_CODES[key] === msgCode)
198    }
199  }
200  
201  module.exports = LES