Home Reference Source Test

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

class BlockProducer {

    constructor(blockchain, accounts, mempool, time) {
        /** @type {BaseChain} */
        this._blockchain = blockchain;
        /** @type {Accounts} */
        this._accounts = accounts;
        /** @type {Mempool} */
        this._mempool = mempool;
        /** @type {Time} */
        this._time = time;
    }


    /**
     * @param {Address} address
     * @param {Uint8Array} [extraData]
     * @return {Promise.<Block>}
     */
    async getNextBlock(address, extraData = new Uint8Array(0)) {
        const nextTarget = await this._blockchain.getNextTarget();
        const interlink = await this._getNextInterlink(nextTarget);
        const body = await this._getNextBody(interlink.serializedSize, address, extraData);
        const header = await this._getNextHeader(nextTarget, interlink, body);
        if (!(await this._blockchain.getNextTarget()).equals(nextTarget)) return this.getNextBlock(address, extraData);
        return new Block(header, interlink, body);
    }

    /**
     * @param {BigNumber} nextTarget
     * @param {BlockInterlink} interlink
     * @param {BlockBody} body
     * @return {Promise.<BlockHeader>}
     * @package
     */
    async _getNextHeader(nextTarget, interlink, body) {
        const prevHash = this._blockchain.headHash;
        const interlinkHash = interlink.hash();
        const height = this._blockchain.height + 1;

        // Compute next accountsHash.
        const accounts = await this._accounts.transaction();
        let accountsHash;
        try {
            await accounts.commitBlockBody(body, height, this._blockchain.transactionCache);
            accountsHash = await accounts.hash();
        } catch (e) {
            throw new Error(`Invalid block body: ${e.message}`);
        } finally {
            await accounts.abort();
        }

        const bodyHash = body.hash();
        const timestamp = this._getNextTimestamp();
        const nBits = BlockUtils.targetToCompact(nextTarget);
        const nonce = 0;
        return new BlockHeader(prevHash, interlinkHash, bodyHash, accountsHash, nBits, height, timestamp, nonce);
    }

    /**
     * @param {BigNumber} nextTarget
     * @returns {Promise.<BlockInterlink>}
     * @package
     */
    _getNextInterlink(nextTarget) {
        return this._blockchain.head.getNextInterlink(nextTarget);
    }

    /**
     * @param {number} interlinkSize
     * @param {Address} address
     * @param {Uint8Array} extraData
     * @return {BlockBody}
     * @package
     */
    async _getNextBody(interlinkSize, address, extraData) {
        const maxSize = Policy.BLOCK_SIZE_MAX
            - BlockHeader.SERIALIZED_SIZE
            - interlinkSize
            - BlockBody.getMetadataSize(extraData);
        const transactions = await this._mempool.getTransactionsForBlock(maxSize);
        const prunedAccounts = await this._accounts.gatherToBePrunedAccounts(transactions, this._blockchain.height + 1, this._blockchain.transactionCache);
        return new BlockBody(address, transactions, extraData, prunedAccounts);
    }

    /**
     * @return {number}
     * @package
     */
    _getNextTimestamp() {
        const now = Math.floor(this._time.now() / 1000);
        return Math.max(now, this._blockchain.head.timestamp + 1);
    }

}

Class.register(BlockProducer);