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