co.rsk.mine.BlockToMineBuilder.java Source code

Java tutorial

Introduction

Here is the source code for co.rsk.mine.BlockToMineBuilder.java

Source

/*
 * This file is part of RskJ
 * Copyright (C) 2018 RSK Labs Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package co.rsk.mine;

import co.rsk.config.MiningConfig;
import co.rsk.config.RskSystemProperties;
import co.rsk.core.Coin;
import co.rsk.core.DifficultyCalculator;
import co.rsk.core.RskAddress;
import co.rsk.core.bc.BlockExecutor;
import co.rsk.core.bc.FamilyUtils;
import co.rsk.remasc.RemascTransaction;
import co.rsk.validators.BlockValidationRule;
import org.apache.commons.collections4.CollectionUtils;
import org.ethereum.core.*;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.BlockStore;
import org.ethereum.db.ReceiptStore;
import org.ethereum.vm.PrecompiledContracts;
import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.util.*;

/**
 * This component helps build a new block to mine.
 * It can also be used to generate a new block from the pending state, which is useful
 * in places like Web3 with the 'pending' parameter.
 */
@Component
public class BlockToMineBuilder {
    private static final Logger logger = LoggerFactory.getLogger("blocktominebuilder");

    private final MiningConfig miningConfig;
    private final Repository repository;
    private final BlockStore blockStore;
    private final TransactionPool transactionPool;
    private final DifficultyCalculator difficultyCalculator;
    private final GasLimitCalculator gasLimitCalculator;
    private final BlockValidationRule validationRules;
    private final MinerClock clock;

    private final MinimumGasPriceCalculator minimumGasPriceCalculator;
    private final MinerUtils minerUtils;
    private final BlockExecutor executor;

    private final Coin minerMinGasPriceTarget;

    @Autowired
    public BlockToMineBuilder(MiningConfig miningConfig, Repository repository, BlockStore blockStore,
            TransactionPool transactionPool, DifficultyCalculator difficultyCalculator,
            GasLimitCalculator gasLimitCalculator,
            @Qualifier("minerServerBlockValidation") BlockValidationRule validationRules,
            RskSystemProperties config, ReceiptStore receiptStore, MinerClock clock) {
        this.miningConfig = Objects.requireNonNull(miningConfig);
        this.repository = Objects.requireNonNull(repository);
        this.blockStore = Objects.requireNonNull(blockStore);
        this.transactionPool = Objects.requireNonNull(transactionPool);
        this.difficultyCalculator = Objects.requireNonNull(difficultyCalculator);
        this.gasLimitCalculator = Objects.requireNonNull(gasLimitCalculator);
        this.validationRules = Objects.requireNonNull(validationRules);
        this.clock = Objects.requireNonNull(clock);
        this.minimumGasPriceCalculator = new MinimumGasPriceCalculator();
        this.minerUtils = new MinerUtils();
        final ProgramInvokeFactoryImpl programInvokeFactory = new ProgramInvokeFactoryImpl();
        this.executor = new BlockExecutor(repository,
                (tx1, txindex1, coinbase, track1, block1, totalGasUsed1) -> new TransactionExecutor(tx1, txindex1,
                        block1.getCoinbase(), track1, blockStore, receiptStore, programInvokeFactory, block1, null,
                        totalGasUsed1, config.getVmConfig(), config.getBlockchainConfig(), config.playVM(),
                        config.isRemascEnabled(), config.vmTrace(), new PrecompiledContracts(config),
                        config.databaseDir(), config.vmTraceDir(), config.vmTraceCompressed()));

        this.minerMinGasPriceTarget = Coin.valueOf(miningConfig.getMinGasPriceTarget());
    }

    /**
     * build creates a block to mine based on the given block as parent.
     *
     * @param newBlockParent the new block parent.
     * @param extraData      extra data to pass to the block being built
     */
    public Block build(Block newBlockParent, byte[] extraData) {
        List<BlockHeader> uncles = FamilyUtils.getUnclesHeaders(blockStore, newBlockParent.getNumber() + 1,
                newBlockParent.getHash().getBytes(), miningConfig.getUncleGenerationLimit());

        if (uncles.size() > miningConfig.getUncleListLimit()) {
            uncles = uncles.subList(0, miningConfig.getUncleListLimit());
        }

        Coin minimumGasPrice = minimumGasPriceCalculator.calculate(newBlockParent.getMinimumGasPrice(),
                minerMinGasPriceTarget);

        final List<Transaction> txsToRemove = new ArrayList<>();
        final List<Transaction> txs = getTransactions(txsToRemove, newBlockParent, minimumGasPrice);
        final Block newBlock = createBlock(newBlockParent, uncles, txs, minimumGasPrice);

        newBlock.setExtraData(extraData);
        removePendingTransactions(txsToRemove);
        executor.executeAndFill(newBlock, newBlockParent);
        return newBlock;
    }

    private List<Transaction> getTransactions(List<Transaction> txsToRemove, Block parent, Coin minGasPrice) {
        logger.debug("getting transactions from pending state");
        List<Transaction> txs = minerUtils.getAllTransactions(transactionPool);
        logger.debug("{} transaction(s) collected from pending state", txs.size());

        Transaction remascTx = new RemascTransaction(parent.getNumber() + 1);
        txs.add(remascTx);

        Map<RskAddress, BigInteger> accountNonces = new HashMap<>();

        Repository originalRepo = repository.getSnapshotTo(parent.getStateRoot());

        return minerUtils.filterTransactions(txsToRemove, txs, accountNonces, originalRepo, minGasPrice);
    }

    private void removePendingTransactions(List<Transaction> transactions) {
        transactionPool.removeTransactions(transactions);
    }

    private Block createBlock(Block newBlockParent, List<BlockHeader> uncles, List<Transaction> txs,
            Coin minimumGasPrice) {
        final BlockHeader newHeader = createHeader(newBlockParent, uncles, txs, minimumGasPrice);
        final Block newBlock = new Block(newHeader, txs, uncles);
        return validationRules.isValid(newBlock) ? newBlock : new Block(newHeader, txs, null);
    }

    private BlockHeader createHeader(Block newBlockParent, List<BlockHeader> uncles, List<Transaction> txs,
            Coin minimumGasPrice) {
        final byte[] unclesListHash = HashUtil.keccak256(BlockHeader.getUnclesEncodedEx(uncles));

        final long timestampSeconds = clock.calculateTimestampForChild(newBlockParent);

        // Set gas limit before executing block
        BigInteger minGasLimit = BigInteger.valueOf(miningConfig.getGasLimit().getMininimum());
        BigInteger targetGasLimit = BigInteger.valueOf(miningConfig.getGasLimit().getTarget());
        BigInteger parentGasLimit = new BigInteger(1, newBlockParent.getGasLimit());
        BigInteger gasUsed = BigInteger.valueOf(newBlockParent.getGasUsed());
        boolean forceLimit = miningConfig.getGasLimit().isTargetForced();
        BigInteger gasLimit = gasLimitCalculator.calculateBlockGasLimit(parentGasLimit, gasUsed, minGasLimit,
                targetGasLimit, forceLimit);

        final BlockHeader newHeader = new BlockHeader(newBlockParent.getHash().getBytes(), unclesListHash,
                miningConfig.getCoinbaseAddress().getBytes(), new Bloom().getData(), new byte[] { 1 },
                newBlockParent.getNumber() + 1, gasLimit.toByteArray(), 0, timestampSeconds, new byte[] {},
                new byte[] {}, new byte[] {}, new byte[] {}, minimumGasPrice.getBytes(),
                CollectionUtils.size(uncles));
        newHeader.setDifficulty(difficultyCalculator.calcDifficulty(newHeader, newBlockParent.getHeader()));
        newHeader.setTransactionsRoot(Block.getTxTrie(txs).getHash().getBytes());
        return newHeader;
    }
}