@neo-one/node-protocol
Version:
NEO•ONE NEO node and consensus protocol.
287 lines (285 loc) • 12.3 kB
JavaScript
"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