@neo-one/node-protocol-esnext-esm
Version:
NEO•ONE NEO node and consensus protocol.
282 lines (280 loc) • 11.2 kB
JavaScript
import { common, createSerializeWire, crypto, InvalidFormatError, IOHelper, } from '@neo-one/client-common-esnext-esm';
import { BinaryReader, Block, ConsensusPayload, deserializeTransactionWire, } from '@neo-one/node-core-esnext-esm';
import { makeErrorWithCode, utils } from '@neo-one/utils-esnext-esm';
import { Transform } from 'stream';
import { assertCommand, Command } from './Command';
import { AddrPayload, FilterAddPayload, FilterLoadPayload, GetBlocksPayload, HeadersPayload, InvPayload, MerkleBlockPayload, VersionPayload, } from './payload';
export const COMMAND_LENGTH = 12;
export const PAYLOAD_MAX_SIZE = 0x02000000;
const calculateChecksum = (buffer) => common.toUInt32LE(crypto.hash256(buffer));
const deserializeMessageHeader = ({ context, reader, }) => {
if (reader.readUInt32LE() !== context.messageMagic) {
throw new InvalidFormatError(`Expected BinaryReader readUInt32LE(0) to equal ${context.messageMagic}. Received: ${context.messageMagic}`);
}
const command = assertCommand(reader.readFixedString(COMMAND_LENGTH));
const length = reader.readUInt32LE();
if (length > PAYLOAD_MAX_SIZE) {
throw new InvalidFormatError(`Expected buffer readout to be less than max payload size of ${PAYLOAD_MAX_SIZE}. Received: ${length}`);
}
const checksum = reader.readUInt32LE();
return { command, length, checksum };
};
export class Message {
constructor({ magic, value }) {
this.serializeWire = 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 InvalidFormatError(`Expected payloadBuffer checksum to be ${checksum}. Received: ${payloadBufferChecksum}`);
}
const payloadOptions = {
context: options.context,
buffer: payloadBuffer,
};
let value;
switch (command) {
case Command.addr:
value = {
command: Command.addr,
payload: AddrPayload.deserializeWire(payloadOptions),
};
break;
case Command.block:
value = {
command: Command.block,
payload: Block.deserializeWire(payloadOptions),
};
break;
case Command.consensus:
value = {
command: Command.consensus,
payload: ConsensusPayload.deserializeWire(payloadOptions),
};
break;
case Command.filteradd:
value = {
command: Command.filteradd,
payload: FilterAddPayload.deserializeWire(payloadOptions),
};
break;
case Command.filterclear:
value = { command: Command.filterclear };
break;
case Command.filterload:
value = {
command: Command.filterload,
payload: FilterLoadPayload.deserializeWire(payloadOptions),
};
break;
case Command.getaddr:
value = { command: Command.getaddr };
break;
case Command.getblocks:
value = {
command: Command.getblocks,
payload: GetBlocksPayload.deserializeWire(payloadOptions),
};
break;
case Command.getdata:
value = {
command: Command.getdata,
payload: InvPayload.deserializeWire(payloadOptions),
};
break;
case Command.getheaders:
value = {
command: Command.getheaders,
payload: GetBlocksPayload.deserializeWire(payloadOptions),
};
break;
case Command.headers:
value = {
command: Command.headers,
payload: HeadersPayload.deserializeWire(payloadOptions),
};
break;
case Command.inv:
value = {
command: Command.inv,
payload: InvPayload.deserializeWire(payloadOptions),
};
break;
case Command.mempool:
value = { command: Command.mempool };
break;
case Command.tx:
value = {
command: Command.tx,
payload: deserializeTransactionWire(payloadOptions),
};
break;
case Command.verack:
value = { command: Command.verack };
break;
case Command.version:
value = {
command: Command.version,
payload: VersionPayload.deserializeWire(payloadOptions),
};
break;
case Command.alert:
value = { command: Command.alert };
break;
case Command.merkleblock:
value = {
command: Command.merkleblock,
payload: MerkleBlockPayload.deserializeWire(payloadOptions),
};
break;
case Command.notfound:
value = { command: Command.notfound };
break;
case Command.ping:
value = { command: Command.ping };
break;
case Command.pong:
value = { command: Command.pong };
break;
case Command.reject:
value = { command: Command.reject };
break;
default:
utils.assertNever(command);
throw new InvalidFormatError(``);
}
return new this({ magic: context.messageMagic, value });
}
static deserializeWire(options) {
return this.deserializeWireBase({
context: options.context,
reader: new BinaryReader(options.buffer),
});
}
serializeWireBase(writer) {
const { value } = this;
writer.writeUInt32LE(this.magic);
writer.writeFixedString(value.command, COMMAND_LENGTH);
let payload = Buffer.alloc(0);
switch (value.command) {
case Command.addr:
payload = value.payload.serializeWire();
break;
case Command.block:
payload = value.payload.serializeWire();
break;
case Command.consensus:
payload = value.payload.serializeWire();
break;
case Command.filteradd:
payload = value.payload.serializeWire();
break;
case Command.filterclear:
break;
case Command.filterload:
payload = value.payload.serializeWire();
break;
case Command.getaddr:
break;
case Command.getblocks:
payload = value.payload.serializeWire();
break;
case Command.getdata:
payload = value.payload.serializeWire();
break;
case Command.getheaders:
payload = value.payload.serializeWire();
break;
case Command.headers:
payload = value.payload.serializeWire();
break;
case Command.inv:
payload = value.payload.serializeWire();
break;
case Command.mempool:
break;
case Command.tx:
payload = value.payload.serializeWire();
break;
case Command.verack:
break;
case Command.version:
payload = value.payload.serializeWire();
break;
case Command.alert:
break;
case Command.merkleblock:
payload = value.payload.serializeWire();
break;
case Command.notfound:
break;
case Command.ping:
break;
case Command.pong:
break;
case Command.reject:
break;
default:
utils.assertNever(value);
throw new InvalidFormatError('Command does not exist');
}
writer.writeUInt32LE(payload.length);
writer.writeUInt32LE(calculateChecksum(payload));
writer.writeBytes(payload);
}
}
export const InvalidMessageTransformEncodingError = makeErrorWithCode('INVALID_MESSAGE_TRANSFORM_ENCODING', (message) => message);
const SIZE_OF_MESSAGE_HEADER = IOHelper.sizeOfUInt32LE +
IOHelper.sizeOfFixedString(COMMAND_LENGTH) +
IOHelper.sizeOfUInt32LE +
IOHelper.sizeOfUInt32LE;
export class MessageTransform extends Transform {
constructor(context) {
super({ readableObjectMode: true });
this.context = context;
this.mutableBuffer = Buffer.from([]);
}
_transform(chunk, encoding, callback) {
if (typeof chunk === 'string') {
throw new InvalidMessageTransformEncodingError(`Invalid Message Transform Chunk Type. Expected chunk type to be 'string', found: ${typeof chunk}`);
}
if (encoding !== 'buffer') {
throw new InvalidMessageTransformEncodingError(`Invalid Message Transform Encoding. Expected: 'buffer', found: ${encoding}`);
}
this.mutableBuffer = Buffer.concat([this.mutableBuffer, chunk]);
try {
const { remainingBuffer, mutableMessages } = this.processBuffer(new 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 };
}
}
//# sourceMappingURL=Message.js.map