Home Reference Source Test

src/main/generic/consensus/base/blockchain/HeaderChain.js

class HeaderChain {
    /**
     * @param {Array.<BlockHeader>} headers
     */
    constructor(headers) {
        if (!headers || !Array.isArray(headers) || !NumberUtils.isUint16(headers.length)
            || headers.some(it => !(it instanceof BlockHeader))) throw new Error('Malformed headers');

        /** @type {Array.<BlockHeader>} */
        this._headers = headers;
    }

    /**
     * @param {SerialBuffer} buf
     * @returns {HeaderChain}
     */
    static unserialize(buf) {
        const count = buf.readUint16();
        const headers = [];
        for (let i = 0; i < count; i++) {
            headers.push(BlockHeader.unserialize(buf));
        }
        return new HeaderChain(headers);
    }

    /**
     * @param {SerialBuffer} [buf]
     * @returns {SerialBuffer}
     */
    serialize(buf) {
        buf = buf || new SerialBuffer(this.serializedSize);
        buf.writeUint16(this._headers.length);
        for (const header of this._headers) {
            header.serialize(buf);
        }
        return buf;
    }

    /** @type {number} */
    get serializedSize() {
        return /*count*/ 2
            + this._headers.reduce((sum, header) => sum + header.serializedSize, 0);
    }

    /**
     * @returns {Promise.<boolean>}
     */
    async verify() {
        // For performance reasons, we DO NOT VERIFY the validity of the blocks in the chain here.
        // Block validity is checked by the Nano/LightChain upon receipt of a ChainProof.

        // Check that all headers in the chain are valid successors of one another.
        for (let i = this._headers.length - 1; i >= 1; i--) {
            if (!this._headers[i].isImmediateSuccessorOf(this._headers[i - 1])) {
                return false;
            }
        }

        // Everything checks out.
        return true;
    }

    /**
     * @returns {string}
     */
    toString() {
        return `HeaderChain{length=${this.length}}`;
    }

    /** @type {number} */
    get length() {
        return this._headers.length;
    }

    /** @type {Array.<BlockHeader>} */
    get headers() {
        return this._headers;
    }

    /** @type {BlockHeader} */
    get head() {
        return this._headers[this.length - 1];
    }

    /** @type {BlockHeader} */
    get tail() {
        return this._headers[0];
    }

    /**
     * @returns {number}
     */
    totalDifficulty() {
        return this._headers.reduce((sum, header) => sum + BlockUtils.targetToDifficulty(header.target), 0);
    }
}
Class.register(HeaderChain);