UNPKG

@neo-one/node-protocol

Version:

NEO•ONE NEO node and consensus protocol.

287 lines (285 loc) 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MessageTransform = exports.InvalidMessageTransformEncodingError = exports.Message = exports.PAYLOAD_MAX_SIZE = exports.COMMAND_LENGTH = void 0; const client_common_1 = require("@neo-one/client-common"); const node_core_1 = require("@neo-one/node-core"); const utils_1 = require("@neo-one/utils"); const stream_1 = require("stream"); const Command_1 = require("./Command"); const payload_1 = require("./payload"); exports.COMMAND_LENGTH = 12; exports.PAYLOAD_MAX_SIZE = 0x02000000; const calculateChecksum = (buffer) => client_common_1.common.toUInt32LE(client_common_1.crypto.hash256(buffer)); const deserializeMessageHeader = ({ context, reader, }) => { if (reader.readUInt32LE() !== context.messageMagic) { throw new client_common_1.InvalidFormatError(`Expected BinaryReader readUInt32LE(0) to equal ${context.messageMagic}. Received: ${context.messageMagic}`); } const command = Command_1.assertCommand(reader.readFixedString(exports.COMMAND_LENGTH)); const length = reader.readUInt32LE(); if (length > exports.PAYLOAD_MAX_SIZE) { throw new client_common_1.InvalidFormatError(`Expected buffer readout to be less than max payload size of ${exports.PAYLOAD_MAX_SIZE}. Received: ${length}`); } const checksum = reader.readUInt32LE(); return { command, length, checksum }; }; class Message { constructor({ magic, value }) { this.serializeWire = client_common_1.createSerializeWire(this.serializeWireBase.bind(this)); this.magic = magic; this.value = value; } static deserializeWireBase(options) { const { reader, context } = options; const { command, length, checksum } = deserializeMessageHeader(options); const payloadBuffer = reader.readBytes(length); const payloadBufferChecksum = calculateChecksum(payloadBuffer); if (payloadBufferChecksum !== checksum) { throw new client_common_1.InvalidFormatError(`Expected payloadBuffer checksum to be ${checksum}. Received: ${payloadBufferChecksum}`); } const payloadOptions = { context: options.context, buffer: payloadBuffer, }; let value; switch (command) { case Command_1.Command.addr: value = { command: Command_1.Command.addr, payload: payload_1.AddrPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.block: value = { command: Command_1.Command.block, payload: node_core_1.Block.deserializeWire(payloadOptions), }; break; case Command_1.Command.consensus: value = { command: Command_1.Command.consensus, payload: node_core_1.ConsensusPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.filteradd: value = { command: Command_1.Command.filteradd, payload: payload_1.FilterAddPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.filterclear: value = { command: Command_1.Command.filterclear }; break; case Command_1.Command.filterload: value = { command: Command_1.Command.filterload, payload: payload_1.FilterLoadPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.getaddr: value = { command: Command_1.Command.getaddr }; break; case Command_1.Command.getblocks: value = { command: Command_1.Command.getblocks, payload: payload_1.GetBlocksPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.getdata: value = { command: Command_1.Command.getdata, payload: payload_1.InvPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.getheaders: value = { command: Command_1.Command.getheaders, payload: payload_1.GetBlocksPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.headers: value = { command: Command_1.Command.headers, payload: payload_1.HeadersPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.inv: value = { command: Command_1.Command.inv, payload: payload_1.InvPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.mempool: value = { command: Command_1.Command.mempool }; break; case Command_1.Command.tx: value = { command: Command_1.Command.tx, payload: node_core_1.deserializeTransactionWire(payloadOptions), }; break; case Command_1.Command.verack: value = { command: Command_1.Command.verack }; break; case Command_1.Command.version: value = { command: Command_1.Command.version, payload: payload_1.VersionPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.alert: value = { command: Command_1.Command.alert }; break; case Command_1.Command.merkleblock: value = { command: Command_1.Command.merkleblock, payload: payload_1.MerkleBlockPayload.deserializeWire(payloadOptions), }; break; case Command_1.Command.notfound: value = { command: Command_1.Command.notfound }; break; case Command_1.Command.ping: value = { command: Command_1.Command.ping }; break; case Command_1.Command.pong: value = { command: Command_1.Command.pong }; break; case Command_1.Command.reject: value = { command: Command_1.Command.reject }; break; default: utils_1.utils.assertNever(command); throw new client_common_1.InvalidFormatError(``); } return new this({ magic: context.messageMagic, value }); } static deserializeWire(options) { return this.deserializeWireBase({ context: options.context, reader: new node_core_1.BinaryReader(options.buffer), }); } serializeWireBase(writer) { const { value } = this; writer.writeUInt32LE(this.magic); writer.writeFixedString(value.command, exports.COMMAND_LENGTH); let payload = Buffer.alloc(0); switch (value.command) { case Command_1.Command.addr: payload = value.payload.serializeWire(); break; case Command_1.Command.block: payload = value.payload.serializeWire(); break; case Command_1.Command.consensus: payload = value.payload.serializeWire(); break; case Command_1.Command.filteradd: payload = value.payload.serializeWire(); break; case Command_1.Command.filterclear: break; case Command_1.Command.filterload: payload = value.payload.serializeWire(); break; case Command_1.Command.getaddr: break; case Command_1.Command.getblocks: payload = value.payload.serializeWire(); break; case Command_1.Command.getdata: payload = value.payload.serializeWire(); break; case Command_1.Command.getheaders: payload = value.payload.serializeWire(); break; case Command_1.Command.headers: payload = value.payload.serializeWire(); break; case Command_1.Command.inv: payload = value.payload.serializeWire(); break; case Command_1.Command.mempool: break; case Command_1.Command.tx: payload = value.payload.serializeWire(); break; case Command_1.Command.verack: break; case Command_1.Command.version: payload = value.payload.serializeWire(); break; case Command_1.Command.alert: break; case Command_1.Command.merkleblock: payload = value.payload.serializeWire(); break; case Command_1.Command.notfound: break; case Command_1.Command.ping: break; case Command_1.Command.pong: break; case Command_1.Command.reject: break; default: utils_1.utils.assertNever(value); throw new client_common_1.InvalidFormatError('Command does not exist'); } writer.writeUInt32LE(payload.length); writer.writeUInt32LE(calculateChecksum(payload)); writer.writeBytes(payload); } } exports.Message = Message; exports.InvalidMessageTransformEncodingError = utils_1.makeErrorWithCode('INVALID_MESSAGE_TRANSFORM_ENCODING', (message) => message); const SIZE_OF_MESSAGE_HEADER = client_common_1.IOHelper.sizeOfUInt32LE + client_common_1.IOHelper.sizeOfFixedString(exports.COMMAND_LENGTH) + client_common_1.IOHelper.sizeOfUInt32LE + client_common_1.IOHelper.sizeOfUInt32LE; class MessageTransform extends stream_1.Transform { constructor(context) { super({ readableObjectMode: true }); this.context = context; this.mutableBuffer = Buffer.from([]); } _transform(chunk, encoding, callback) { if (typeof chunk === 'string') { throw new exports.InvalidMessageTransformEncodingError(`Invalid Message Transform Chunk Type. Expected chunk type to be 'string', found: ${typeof chunk}`); } if (encoding !== 'buffer') { throw new exports.InvalidMessageTransformEncodingError(`Invalid Message Transform Encoding. Expected: 'buffer', found: ${encoding}`); } this.mutableBuffer = Buffer.concat([this.mutableBuffer, chunk]); try { const { remainingBuffer, mutableMessages } = this.processBuffer(new node_core_1.BinaryReader(this.mutableBuffer)); this.mutableBuffer = remainingBuffer; mutableMessages.forEach((message) => this.push(message)); callback(undefined); } catch (error) { callback(error); } } processBuffer(reader) { if (reader.remaining < SIZE_OF_MESSAGE_HEADER) { return { remainingBuffer: reader.remainingBuffer, mutableMessages: [] }; } const { length } = deserializeMessageHeader({ context: this.context, reader: reader.clone(), }); if (reader.remaining < SIZE_OF_MESSAGE_HEADER + length) { return { remainingBuffer: reader.remainingBuffer, mutableMessages: [] }; } const message = Message.deserializeWireBase({ context: this.context, reader, }); const { remainingBuffer, mutableMessages } = this.processBuffer(reader); mutableMessages.push(message); return { remainingBuffer, mutableMessages }; } } exports.MessageTransform = MessageTransform; //# sourceMappingURL=Message.js.map