Home Reference Source Test

src/main/generic/network/message/Message.js

class Message {
    /**
     * Create a new Message instance. This is usually not called directly but by subclasses.
     * @param {Message.Type} type Message type
     */
    constructor(type) {
        if (!NumberUtils.isUint64(type)) throw new Error('Malformed type');
        /** @type {Message.Type} */
        this._type = type;
    }

    /**
     * @param {SerialBuffer} buf
     * @returns {Message.Type}
     */
    static peekType(buf) {
        // Store current read position.
        const pos = buf.readPos;

        // Set read position past the magic to the beginning of the type string.
        buf.readPos = 4;

        // Read the type.
        const type = buf.readVarUint();

        // Reset the read position to original.
        buf.readPos = pos;

        return /** @type {Message.Type} */ type;
    }

    /**
     * @param {SerialBuffer} buf
     * @returns {number}
     */
    static peekLength(buf) {
        // Store current read position.
        const pos = buf.readPos;

        // Set read position past the magic to the beginning of the type string.
        buf.readPos = 4;

        // Read the type and ignore it.
        buf.readVarUint();
        // Read the length.
        const length = buf.readUint32();

        // Reset the read position to original.
        buf.readPos = pos;

        return length;
    }

    /**
     * @param {SerialBuffer} buf
     * @returns {Message}
     */
    static unserialize(buf) {
        // XXX Direct buffer manipulation currently requires this.
        Assert.that(buf.readPos === 0, 'Message.unserialize() requires buf.readPos == 0');

        const magic = buf.readUint32();
        const type = buf.readVarUint();
        buf.readUint32(); // length is ignored
        const checksum = buf.readUint32();

        // Validate magic.
        if (magic !== Message.MAGIC) throw 'Malformed magic';

        // Validate checksum.
        Message._writeChecksum(type, buf, 0);
        const calculatedChecksum = CRC32.compute(buf);
        if (checksum !== calculatedChecksum) throw new Error('Invalid checksum');

        return new Message(type);
    }

    /**
     * @param {SerialBuffer} [buf]
     * @returns {SerialBuffer}
     */
    serialize(buf) {
        buf = buf || new SerialBuffer(this.serializedSize);
        // XXX Direct buffer manipulation currently requires this.
        Assert.that(buf.writePos === 0, 'Message.serialize() requires buf.writePos == 0');

        buf.writeUint32(Message.MAGIC);
        buf.writeVarUint(this._type);
        buf.writeUint32(this.serializedSize);
        buf.writeUint32(0); // written later by _setChecksum()

        return buf;
    }

    /** @type {number} */
    get serializedSize() {
        return /*magic*/ 4
            + /*type*/ SerialBuffer.varUintSize(this._type)
            + /*length*/ 4
            + /*checksum*/ 4;
    }

    /**
     * @param {SerialBuffer} buf
     * @returns {void}
     * @protected
     */
    _setChecksum(buf) {
        const checksum = CRC32.compute(buf);
        Message._writeChecksum(this._type, buf, checksum);
    }

    /**
     * @param {Message.Type} type
     * @param {SerialBuffer} buf
     * @param {number} value
     * @returns {void}
     * @private
     */
    static _writeChecksum(type, buf, value) {
        // Store current write position.
        const pos = buf.writePos;

        // Set write position past the magic, type, and length fields to the
        // beginning of the checksum value.
        buf.writePos = /*magic*/ 4
            + /*type*/ SerialBuffer.varUintSize(type)
            + /*length*/ 4;

        // Write the checksum value.
        buf.writeUint32(value);

        // Reset the write position to original.
        buf.writePos = pos;
    }

    /** @type {Message.Type} */
    get type() {
        return this._type;
    }

    /** @returns {string} */
    toString() {
        return `Message{type=${this.type}, size=${this.serializedSize}}`;
    }
}
Message.MAGIC = 0x42042042;
/**
 * Enum for message types.
 * @enum {number}
 */
Message.Type = {
    VERSION:    0,
    INV:        1,
    GET_DATA:   2,
    GET_HEADER: 3,
    NOT_FOUND:  4,
    GET_BLOCKS: 5,
    BLOCK:      6,
    HEADER:     7,
    TX:         8,
    MEMPOOL:    9,
    REJECT:     10,
    SUBSCRIBE:  11,

    ADDR:       20,
    GET_ADDR:   21,
    PING:       22,
    PONG:       23,

    SIGNAL:     30,

    GET_CHAIN_PROOF:            40,
    CHAIN_PROOF:                41,
    GET_ACCOUNTS_PROOF:         42,
    ACCOUNTS_PROOF:             43,
    GET_ACCOUNTS_TREE_CHUNK:    44,
    ACCOUNTS_TREE_CHUNK:        45,
    GET_TRANSACTIONS_PROOF:     47,
    TRANSACTIONS_PROOF:         48,
    GET_TRANSACTION_RECEIPTS:   49,
    TRANSACTION_RECEIPTS:       50,
    GET_BLOCK_PROOF:            51,
    BLOCK_PROOF:                52,

    GET_HEAD:   60,
    HEAD:       61,

    VERACK:   90
};
Class.register(Message);