Coverage Summary for Class: BlockToMineBuilder (co.rsk.mine)

Class Class, % Method, % Line, %
BlockToMineBuilder 0% (0/1) 0% (0/7) 0% (0/78)


1 /* 2  * This file is part of RskJ 3  * Copyright (C) 2018 RSK Labs Ltd. 4  * 5  * This program is free software: you can redistribute it and/or modify 6  * it under the terms of the GNU Lesser General Public License as published by 7  * the Free Software Foundation, either version 3 of the License, or 8  * (at your option) any later version. 9  * 10  * This program is distributed in the hope that it will be useful, 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13  * GNU Lesser General Public License for more details. 14  * 15  * You should have received a copy of the GNU Lesser General Public License 16  * along with this program. If not, see <http://www.gnu.org/licenses/>. 17  */ 18  19 package co.rsk.mine; 20  21 import co.rsk.config.MiningConfig; 22 import co.rsk.core.BlockDifficulty; 23 import co.rsk.core.Coin; 24 import co.rsk.core.DifficultyCalculator; 25 import co.rsk.core.RskAddress; 26 import co.rsk.core.bc.BlockExecutor; 27 import co.rsk.core.bc.BlockHashesHelper; 28 import co.rsk.core.bc.BlockResult; 29 import co.rsk.core.bc.FamilyUtils; 30 import co.rsk.db.RepositoryLocator; 31 import co.rsk.db.RepositorySnapshot; 32 import co.rsk.panic.PanicProcessor; 33 import co.rsk.remasc.RemascTransaction; 34 import co.rsk.validators.BlockValidationRule; 35 import com.google.common.annotations.VisibleForTesting; 36 import org.ethereum.config.blockchain.upgrades.ActivationConfig; 37 import org.ethereum.config.blockchain.upgrades.ConsensusRule; 38 import org.ethereum.core.*; 39 import org.ethereum.crypto.HashUtil; 40 import org.ethereum.db.BlockStore; 41 import org.slf4j.Logger; 42 import org.slf4j.LoggerFactory; 43  44 import java.math.BigInteger; 45 import java.util.*; 46  47 /** 48  * This component helps build a new block to mine. 49  * It can also be used to generate a new block from the pending state, which is useful 50  * in places like Web3 with the 'pending' parameter. 51  */ 52 public class BlockToMineBuilder { 53  private static final Logger logger = LoggerFactory.getLogger("blocktominebuilder"); 54  55  private final ActivationConfig activationConfig; 56  private final MiningConfig miningConfig; 57  private final RepositoryLocator repositoryLocator; 58  private final BlockStore blockStore; 59  private final TransactionPool transactionPool; 60  private final DifficultyCalculator difficultyCalculator; 61  private final GasLimitCalculator gasLimitCalculator; 62  private final BlockValidationRule validationRules; 63  private final MinerClock clock; 64  private final BlockFactory blockFactory; 65  private final BlockExecutor executor; 66  private static final PanicProcessor panicProcessor = new PanicProcessor(); 67  68  private final MinimumGasPriceCalculator minimumGasPriceCalculator; 69  private final MinerUtils minerUtils; 70  71  private final ForkDetectionDataCalculator forkDetectionDataCalculator; 72  73  public BlockToMineBuilder( 74  ActivationConfig activationConfig, 75  MiningConfig miningConfig, 76  RepositoryLocator repositoryLocator, 77  BlockStore blockStore, 78  TransactionPool transactionPool, 79  DifficultyCalculator difficultyCalculator, 80  GasLimitCalculator gasLimitCalculator, 81  ForkDetectionDataCalculator forkDetectionDataCalculator, 82  BlockValidationRule validationRules, 83  MinerClock clock, 84  BlockFactory blockFactory, 85  BlockExecutor blockExecutor, 86  MinimumGasPriceCalculator minimumGasPriceCalculator, 87  MinerUtils minerUtils) { 88  this.activationConfig = Objects.requireNonNull(activationConfig); 89  this.miningConfig = Objects.requireNonNull(miningConfig); 90  this.repositoryLocator = Objects.requireNonNull(repositoryLocator); 91  this.blockStore = Objects.requireNonNull(blockStore); 92  this.transactionPool = Objects.requireNonNull(transactionPool); 93  this.difficultyCalculator = Objects.requireNonNull(difficultyCalculator); 94  this.gasLimitCalculator = Objects.requireNonNull(gasLimitCalculator); 95  this.forkDetectionDataCalculator = Objects.requireNonNull(forkDetectionDataCalculator); 96  this.validationRules = Objects.requireNonNull(validationRules); 97  this.clock = Objects.requireNonNull(clock); 98  this.blockFactory = blockFactory; 99  this.executor = blockExecutor; 100  this.minimumGasPriceCalculator = minimumGasPriceCalculator; 101  this.minerUtils = minerUtils; 102  } 103  104  /** 105  * Creates a new block to mine based on the previous mainchain blocks. 106  * 107  * @param mainchainHeaders last best chain blocks where 0 index is the best block and so on. 108  * @param extraData extra data to pass to the block being built. 109  */ 110  public BlockResult build(List<BlockHeader> mainchainHeaders, byte[] extraData) { 111  BlockHeader newBlockParentHeader = mainchainHeaders.get(0); 112  List<BlockHeader> uncles = FamilyUtils.getUnclesHeaders( 113  blockStore, 114  newBlockParentHeader.getNumber() + 1, 115  newBlockParentHeader.getHash(), 116  miningConfig.getUncleGenerationLimit() 117  ); 118  119  120  if (uncles.size() > miningConfig.getUncleListLimit()) { 121  uncles = uncles.subList(0, miningConfig.getUncleListLimit()); 122  } 123  124  Coin minimumGasPrice = minimumGasPriceCalculator.calculate(newBlockParentHeader.getMinimumGasPrice()); 125  126  final List<Transaction> txsToRemove = new ArrayList<>(); 127  final List<Transaction> txs = getTransactions(txsToRemove, newBlockParentHeader, minimumGasPrice); 128  final Block newBlock = createBlock(mainchainHeaders, uncles, txs, minimumGasPrice, extraData); 129  130  removePendingTransactions(txsToRemove); 131  return executor.executeAndFill(newBlock, newBlockParentHeader); 132  } 133  134  private List<Transaction> getTransactions(List<Transaction> txsToRemove, BlockHeader parentHeader, Coin minGasPrice) { 135  logger.debug("getting transactions from pending state"); 136  List<Transaction> txs = minerUtils.getAllTransactions(transactionPool); 137  logger.debug("{} transaction(s) collected from pending state", txs.size()); 138  139  Transaction remascTx = new RemascTransaction(parentHeader.getNumber() + 1); 140  txs.add(remascTx); 141  142  Map<RskAddress, BigInteger> accountNonces = new HashMap<>(); 143  144  RepositorySnapshot originalRepo = repositoryLocator.snapshotAt(parentHeader); 145  146  return minerUtils.filterTransactions(txsToRemove, txs, accountNonces, originalRepo, minGasPrice); 147  } 148  149  private void removePendingTransactions(List<Transaction> transactions) { 150  transactionPool.removeTransactions(transactions); 151  } 152  153  @VisibleForTesting 154  public Block createBlock( 155  List<BlockHeader> mainchainHeaders, 156  List<BlockHeader> uncles, 157  List<Transaction> txs, 158  Coin minimumGasPrice, 159  byte[] extraData) { 160  BlockHeader newHeader = createHeader(mainchainHeaders, uncles, txs, minimumGasPrice, extraData); 161  Block newBlock = blockFactory.newBlock(newHeader, txs, uncles, false); 162  163  // TODO(nacho): The validation rules should accept a list of uncles and we should never build invalid blocks. 164  if (validationRules.isValid(newBlock)) { 165  return newBlock; 166  } 167  168  // Some validation rule failed (all validations run are related with uncles rules), 169  // log the panic, and create again the block without uncles to avoid fail abruptly. 170  panicProcessor.panic("buildBlock", "some validation failed trying to create a new block"); 171  172  newHeader = createHeader(mainchainHeaders, Collections.emptyList(), txs, minimumGasPrice, extraData); 173  return blockFactory.newBlock(newHeader, txs, Collections.emptyList(), false); 174  } 175  176  private BlockHeader createHeader( 177  List<BlockHeader> mainchainHeaders, 178  List<BlockHeader> uncles, 179  List<Transaction> txs, 180  Coin minimumGasPrice, 181  byte[] extraData) { 182  final byte[] unclesListHash = HashUtil.keccak256(BlockHeader.getUnclesEncodedEx(uncles)); 183  184  BlockHeader newBlockParentHeader = mainchainHeaders.get(0); 185  final long timestampSeconds = clock.calculateTimestampForChild(newBlockParentHeader); 186  187  // Set gas limit before executing block 188  BigInteger minGasLimit = BigInteger.valueOf(miningConfig.getGasLimit().getMininimum()); 189  BigInteger targetGasLimit = BigInteger.valueOf(miningConfig.getGasLimit().getTarget()); 190  BigInteger parentGasLimit = new BigInteger(1, newBlockParentHeader.getGasLimit()); 191  BigInteger gasUsed = BigInteger.valueOf(newBlockParentHeader.getGasUsed()); 192  boolean forceLimit = miningConfig.getGasLimit().isTargetForced(); 193  BigInteger gasLimit = gasLimitCalculator.calculateBlockGasLimit(parentGasLimit, 194  gasUsed, minGasLimit, targetGasLimit, forceLimit); 195  byte[] forkDetectionData = forkDetectionDataCalculator.calculateWithBlockHeaders(mainchainHeaders); 196  197  long blockNumber = newBlockParentHeader.getNumber() + 1; 198  199  // ummRoot can not be set to a value yet since the UMM contracts are not yet implemented 200  byte[] ummRoot = activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber) ? new byte[0] : null; 201  202  final BlockHeader newHeader = blockFactory 203  .getBlockHeaderBuilder() 204  .setParentHash(newBlockParentHeader.getHash().getBytes()) 205  .setUnclesHash(unclesListHash) 206  .setCoinbase(miningConfig.getCoinbaseAddress()) 207  .setTxTrieRoot(BlockHashesHelper.getTxTrieRoot( 208  txs, activationConfig.isActive(ConsensusRule.RSKIP126, blockNumber) 209  ) 210  ) 211  .setDifficulty(BlockDifficulty.ONE) 212  .setNumber(blockNumber) 213  .setGasLimit(gasLimit.toByteArray()) 214  .setGasUsed(0) 215  .setTimestamp(timestampSeconds) 216  .setExtraData(extraData) 217  .setMergedMiningForkDetectionData(forkDetectionData) 218  .setMinimumGasPrice(minimumGasPrice) 219  .setUncleCount(uncles.size()) 220  .setUmmRoot(ummRoot) 221  .build(); 222  223  newHeader.setDifficulty(difficultyCalculator.calcDifficulty(newHeader, newBlockParentHeader)); 224  return newHeader; 225  } 226 }