Coverage Summary for Class: BlockHeader (org.ethereum.core)

Class Method, % Line, %
BlockHeader 81.5% (44/54) 65.4% (140/214)
BlockHeader$MockitoMock$2114669883
BlockHeader$MockitoMock$2114669883$auxiliary$0GQ3aGq3
BlockHeader$MockitoMock$2114669883$auxiliary$1AjaWhht
BlockHeader$MockitoMock$2114669883$auxiliary$1vOZ6Ftp
BlockHeader$MockitoMock$2114669883$auxiliary$1XGydk17
BlockHeader$MockitoMock$2114669883$auxiliary$20MgtjaX
BlockHeader$MockitoMock$2114669883$auxiliary$26K7kPpg
BlockHeader$MockitoMock$2114669883$auxiliary$2EKOaQGz
BlockHeader$MockitoMock$2114669883$auxiliary$37wrXKFH
BlockHeader$MockitoMock$2114669883$auxiliary$6a7PNjUx
BlockHeader$MockitoMock$2114669883$auxiliary$6b5NCHuU
BlockHeader$MockitoMock$2114669883$auxiliary$7CyzzYIe
BlockHeader$MockitoMock$2114669883$auxiliary$AFbx2wxk
BlockHeader$MockitoMock$2114669883$auxiliary$c2Nz0gPA
BlockHeader$MockitoMock$2114669883$auxiliary$caNhYUhY
BlockHeader$MockitoMock$2114669883$auxiliary$cD9VRh6h
BlockHeader$MockitoMock$2114669883$auxiliary$CDbT7WmL
BlockHeader$MockitoMock$2114669883$auxiliary$eHm9R6jV
BlockHeader$MockitoMock$2114669883$auxiliary$elpYuHH9
BlockHeader$MockitoMock$2114669883$auxiliary$fdAjDpZ3
BlockHeader$MockitoMock$2114669883$auxiliary$fr4Y8bBK
BlockHeader$MockitoMock$2114669883$auxiliary$GPbsk3J5
BlockHeader$MockitoMock$2114669883$auxiliary$HoGeRtU2
BlockHeader$MockitoMock$2114669883$auxiliary$iNC6OxB7
BlockHeader$MockitoMock$2114669883$auxiliary$IX2hqmBA
BlockHeader$MockitoMock$2114669883$auxiliary$izg9rlTu
BlockHeader$MockitoMock$2114669883$auxiliary$JA9yrlqt
BlockHeader$MockitoMock$2114669883$auxiliary$JKxjuxd2
BlockHeader$MockitoMock$2114669883$auxiliary$KLxZtNrJ
BlockHeader$MockitoMock$2114669883$auxiliary$LqALKVQn
BlockHeader$MockitoMock$2114669883$auxiliary$lQn1Yeh0
BlockHeader$MockitoMock$2114669883$auxiliary$mTIeDPOl
BlockHeader$MockitoMock$2114669883$auxiliary$Mv8HjIf2
BlockHeader$MockitoMock$2114669883$auxiliary$ng2ThLb9
BlockHeader$MockitoMock$2114669883$auxiliary$NpBDMdX7
BlockHeader$MockitoMock$2114669883$auxiliary$OfM5lRiI
BlockHeader$MockitoMock$2114669883$auxiliary$PJLmLJXB
BlockHeader$MockitoMock$2114669883$auxiliary$PzdxpOXu
BlockHeader$MockitoMock$2114669883$auxiliary$q3pO5GOR
BlockHeader$MockitoMock$2114669883$auxiliary$R91alXnY
BlockHeader$MockitoMock$2114669883$auxiliary$r934QJmS
BlockHeader$MockitoMock$2114669883$auxiliary$RTzjU4i2
BlockHeader$MockitoMock$2114669883$auxiliary$Sjw62yUc
BlockHeader$MockitoMock$2114669883$auxiliary$TJRBUYE9
BlockHeader$MockitoMock$2114669883$auxiliary$u79z1heR
BlockHeader$MockitoMock$2114669883$auxiliary$UBgP79VD
BlockHeader$MockitoMock$2114669883$auxiliary$xk1YsTRu
BlockHeader$MockitoMock$2114669883$auxiliary$xNpGYw1L
BlockHeader$MockitoMock$2114669883$auxiliary$YDiLOux7
BlockHeader$MockitoMock$2114669883$auxiliary$YummUeS5
Total 81.5% (44/54) 65.4% (140/214)


1 /* 2  * This file is part of RskJ 3  * Copyright (C) 2017 RSK Labs Ltd. 4  * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>) 5  * 6  * This program is free software: you can redistribute it and/or modify 7  * it under the terms of the GNU Lesser General Public License as published by 8  * the Free Software Foundation, either version 3 of the License, or 9  * (at your option) any later version. 10  * 11  * This program is distributed in the hope that it will be useful, 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14  * GNU Lesser General Public License for more details. 15  * 16  * You should have received a copy of the GNU Lesser General Public License 17  * along with this program. If not, see <http://www.gnu.org/licenses/>. 18  */ 19 package org.ethereum.core; 20  21 import co.rsk.config.RskMiningConstants; 22 import co.rsk.core.BlockDifficulty; 23 import co.rsk.core.Coin; 24 import co.rsk.core.RskAddress; 25 import co.rsk.crypto.Keccak256; 26 import co.rsk.util.ListArrayUtil; 27 import com.google.common.annotations.VisibleForTesting; 28 import com.google.common.collect.Lists; 29 import org.ethereum.crypto.HashUtil; 30 import org.ethereum.util.RLP; 31 import org.ethereum.util.Utils; 32  33 import javax.annotation.Nullable; 34 import java.math.BigInteger; 35 import java.util.Arrays; 36 import java.util.List; 37  38 import static java.lang.System.arraycopy; 39 import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH; 40 import static org.ethereum.util.ByteUtil.toHexStringOrEmpty; 41  42 /** 43  * Block header is a value object containing 44  * the basic information of a block 45  */ 46 public class BlockHeader { 47  48  private static final int HASH_FOR_MERGED_MINING_PREFIX_LENGTH = 20; 49  private static final int FORK_DETECTION_DATA_LENGTH = 12; 50  private static final int UMM_LEAVES_LENGTH = 20; 51  52  /* The SHA3 256-bit hash of the parent block, in its entirety */ 53  private byte[] parentHash; 54  /* The SHA3 256-bit hash of the uncles list portion of this block */ 55  private byte[] unclesHash; 56  /* The 160-bit address to which all fees collected from the 57  * successful mining of this block be transferred; formally */ 58  private RskAddress coinbase; 59  /* The SHA3 256-bit hash of the root node of the state trie, 60  * after all transactions are executed and finalisations applied */ 61  private byte[] stateRoot; 62  /* The SHA3 256-bit hash of the root node of the trie structure 63  * populated with each transaction in the transaction 64  * list portion, the trie is populate by [key, val] --> [rlp(index), rlp(tx_recipe)] 65  * of the block */ 66  private byte[] txTrieRoot; 67  /* The SHA3 256-bit hash of the root node of the trie structure 68  * populated with each transaction recipe in the transaction recipes 69  * list portion, the trie is populate by [key, val] --> [rlp(index), rlp(tx_recipe)] 70  * of the block */ 71  private byte[] receiptTrieRoot; 72  /* The bloom filter for the logs of the block */ 73  private byte[] logsBloom; 74  /** 75  * A scalar value corresponding to the difficulty level of this block. 76  * This can be calculated from the previous block’s difficulty level 77  * and the timestamp. 78  */ 79  private BlockDifficulty difficulty; 80  /* A scalar value equalBytes to the reasonable output of Unix's time() 81  * at this block's inception */ 82  private long timestamp; 83  /* A scalar value equalBytes to the number of ancestor blocks. 84  * The genesis block has a number of zero */ 85  private long number; 86  /* A scalar value equalBytes to the current limit of gas expenditure per block */ 87  private byte[] gasLimit; 88  /* A scalar value equalBytes to the total gas used in transactions in this block */ 89  private long gasUsed; 90  /* A scalar value equalBytes to the total paid fees in transactions in this block */ 91  private Coin paidFees; 92  93  /* An arbitrary byte array containing data relevant to this block. 94  * With the exception of the genesis block, this must be 32 bytes or fewer */ 95  private byte[] extraData; 96  97  /* The 80-byte bitcoin block header for merged mining */ 98  private byte[] bitcoinMergedMiningHeader; 99  /* The bitcoin merkle proof of coinbase tx for merged mining */ 100  private byte[] bitcoinMergedMiningMerkleProof; 101  /* The bitcoin protobuf serialized coinbase tx for merged mining */ 102  private byte[] bitcoinMergedMiningCoinbaseTransaction; 103  104  private byte[] miningForkDetectionData; 105  106  private byte[] ummRoot; 107  108  /** 109  * The mgp for a tx to be included in the block. 110  */ 111  private Coin minimumGasPrice; 112  private int uncleCount; 113  114  /* Indicates if this block header cannot be changed */ 115  private volatile boolean sealed; 116  117  /* Indicates if the block was mined according to RSKIP-92 rules */ 118  private boolean useRskip92Encoding; 119  120  /* Indicates if Block hash for merged mining should have the format described in RSKIP-110 */ 121  private boolean includeForkDetectionData; 122  123  public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, 124  byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] logsBloom, BlockDifficulty difficulty, 125  long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, 126  Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, 127  byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, 128  Coin minimumGasPrice, int uncleCount, boolean sealed, 129  boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot) { 130  this.parentHash = parentHash; 131  this.unclesHash = unclesHash; 132  this.coinbase = coinbase; 133  this.stateRoot = stateRoot; 134  this.txTrieRoot = txTrieRoot; 135  this.receiptTrieRoot = receiptTrieRoot; 136  this.logsBloom = logsBloom; 137  this.difficulty = difficulty; 138  this.number = number; 139  this.gasLimit = gasLimit; 140  this.gasUsed = gasUsed; 141  this.timestamp = timestamp; 142  this.extraData = extraData; 143  this.minimumGasPrice = minimumGasPrice; 144  this.uncleCount = uncleCount; 145  this.paidFees = paidFees; 146  this.bitcoinMergedMiningHeader = bitcoinMergedMiningHeader; 147  this.bitcoinMergedMiningMerkleProof = bitcoinMergedMiningMerkleProof; 148  this.bitcoinMergedMiningCoinbaseTransaction = bitcoinMergedMiningCoinbaseTransaction; 149  this.miningForkDetectionData = 150  Arrays.copyOf(mergedMiningForkDetectionData, mergedMiningForkDetectionData.length); 151  this.sealed = sealed; 152  this.useRskip92Encoding = useRskip92Encoding; 153  this.includeForkDetectionData = includeForkDetectionData; 154  this.ummRoot = ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; 155  } 156  157  @VisibleForTesting 158  public boolean isSealed() { 159  return this.sealed; 160  } 161  162  public void seal() { 163  this.sealed = true; 164  } 165  166  public boolean isGenesis() { 167  return this.getNumber() == Genesis.NUMBER; 168  } 169  170  public Keccak256 getParentHash() { 171  return new Keccak256(parentHash); 172  } 173  174  public int getUncleCount() { 175  return uncleCount; 176  } 177  178  public byte[] getUnclesHash() { 179  return unclesHash; 180  } 181  182  public RskAddress getCoinbase() { 183  return this.coinbase; 184  } 185  186  public byte[] getStateRoot() { 187  return stateRoot; 188  } 189  190  public void setStateRoot(byte[] stateRoot) { 191  /* A sealed block header is immutable, cannot be changed */ 192  if (this.sealed) { 193  throw new SealedBlockHeaderException("trying to alter state root"); 194  } 195  196  this.stateRoot = stateRoot; 197  } 198  199  public byte[] getTxTrieRoot() { 200  return txTrieRoot; 201  } 202  203  public void setReceiptsRoot(byte[] receiptTrieRoot) { 204  /* A sealed block header is immutable, cannot be changed */ 205  if (this.sealed) { 206  throw new SealedBlockHeaderException("trying to alter receipts root"); 207  } 208  209  this.receiptTrieRoot = receiptTrieRoot; 210  } 211  212  public byte[] getReceiptsRoot() { 213  return receiptTrieRoot; 214  } 215  216  public void setTransactionsRoot(byte[] stateRoot) { 217  /* A sealed block header is immutable, cannot be changed */ 218  if (this.sealed) { 219  throw new SealedBlockHeaderException("trying to alter transactions root"); 220  } 221  222  this.txTrieRoot = stateRoot; 223  } 224  225  226  public byte[] getLogsBloom() { 227  return logsBloom; 228  } 229  230  public BlockDifficulty getDifficulty() { 231  // some blocks have zero encoded as null, but if we altered the internal field then re-encoding the value would 232  // give a different value than the original. 233  if (difficulty == null) { 234  return BlockDifficulty.ZERO; 235  } 236  237  return difficulty; 238  } 239  240  public void setDifficulty(BlockDifficulty difficulty) { 241  /* A sealed block header is immutable, cannot be changed */ 242  if (this.sealed) { 243  throw new SealedBlockHeaderException("trying to alter difficulty"); 244  } 245  246  this.difficulty = difficulty; 247  } 248  249  public long getTimestamp() { 250  return timestamp; 251  } 252  253  public long getNumber() { 254  return number; 255  } 256  257  public byte[] getGasLimit() { 258  return gasLimit; 259  } 260  261  public long getGasUsed() { 262  return gasUsed; 263  } 264  265  public void setPaidFees(Coin paidFees) { 266  /* A sealed block header is immutable, cannot be changed */ 267  if (this.sealed) { 268  throw new SealedBlockHeaderException("trying to alter paid fees"); 269  } 270  271  this.paidFees = paidFees; 272  } 273  274  public Coin getPaidFees() { 275  return this.paidFees; 276  } 277  278  public void setGasUsed(long gasUsed) { 279  /* A sealed block header is immutable, cannot be changed */ 280  if (this.sealed) { 281  throw new SealedBlockHeaderException("trying to alter gas used"); 282  } 283  284  this.gasUsed = gasUsed; 285  } 286  287  public byte[] getExtraData() { 288  return extraData; 289  } 290  291  public void setLogsBloom(byte[] logsBloom) { 292  /* A sealed block header is immutable, cannot be changed */ 293  if (this.sealed) { 294  throw new SealedBlockHeaderException("trying to alter logs bloom"); 295  } 296  297  this.logsBloom = logsBloom; 298  } 299  300  public Keccak256 getHash() { 301  return new Keccak256(HashUtil.keccak256(getEncoded())); 302  } 303  304  public byte[] getFullEncoded() { 305  // the encoded block header must include all fields, even the bitcoin PMT and coinbase which are not used for 306  // calculating RSKIP92 block hashes 307  return this.getEncoded(true, true); 308  } 309  310  public byte[] getEncoded() { 311  // the encoded block header used for calculating block hashes including RSKIP92 312  return this.getEncoded(true, !useRskip92Encoding); 313  } 314  315  @Nullable 316  public Coin getMinimumGasPrice() { 317  return this.minimumGasPrice; 318  } 319  320  public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProofAndCoinbase) { 321  byte[] parentHash = RLP.encodeElement(this.parentHash); 322  323  byte[] unclesHash = RLP.encodeElement(this.unclesHash); 324  byte[] coinbase = RLP.encodeRskAddress(this.coinbase); 325  326  byte[] stateRoot = RLP.encodeElement(this.stateRoot); 327  328  if (txTrieRoot == null) { 329  this.txTrieRoot = EMPTY_TRIE_HASH; 330  } 331  332  byte[] txTrieRoot = RLP.encodeElement(this.txTrieRoot); 333  334  if (receiptTrieRoot == null) { 335  this.receiptTrieRoot = EMPTY_TRIE_HASH; 336  } 337  338  byte[] receiptTrieRoot = RLP.encodeElement(this.receiptTrieRoot); 339  340  byte[] logsBloom = RLP.encodeElement(this.logsBloom); 341  byte[] difficulty = encodeBlockDifficulty(this.difficulty); 342  byte[] number = RLP.encodeBigInteger(BigInteger.valueOf(this.number)); 343  byte[] gasLimit = RLP.encodeElement(this.gasLimit); 344  byte[] gasUsed = RLP.encodeBigInteger(BigInteger.valueOf(this.gasUsed)); 345  byte[] timestamp = RLP.encodeBigInteger(BigInteger.valueOf(this.timestamp)); 346  byte[] extraData = RLP.encodeElement(this.extraData); 347  byte[] paidFees = RLP.encodeCoin(this.paidFees); 348  byte[] mgp = RLP.encodeSignedCoinNonNullZero(this.minimumGasPrice); 349  List<byte[]> fieldToEncodeList = Lists.newArrayList(parentHash, unclesHash, coinbase, 350  stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number, 351  gasLimit, gasUsed, timestamp, extraData, paidFees, mgp); 352  353  byte[] uncleCount = RLP.encodeBigInteger(BigInteger.valueOf(this.uncleCount)); 354  fieldToEncodeList.add(uncleCount); 355  356  if (this.ummRoot != null) { 357  fieldToEncodeList.add(RLP.encodeElement(this.ummRoot)); 358  } 359  360  if (withMergedMiningFields && hasMiningFields()) { 361  byte[] bitcoinMergedMiningHeader = RLP.encodeElement(this.bitcoinMergedMiningHeader); 362  fieldToEncodeList.add(bitcoinMergedMiningHeader); 363  if (withMerkleProofAndCoinbase) { 364  byte[] bitcoinMergedMiningMerkleProof = RLP.encodeElement(this.bitcoinMergedMiningMerkleProof); 365  fieldToEncodeList.add(bitcoinMergedMiningMerkleProof); 366  byte[] bitcoinMergedMiningCoinbaseTransaction = RLP.encodeElement(this.bitcoinMergedMiningCoinbaseTransaction); 367  fieldToEncodeList.add(bitcoinMergedMiningCoinbaseTransaction); 368  } 369  } 370  371  return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); 372  } 373  374  /** 375  * This is here to override specific non-minimal instances such as the mainnet Genesis 376  */ 377  protected byte[] encodeBlockDifficulty(BlockDifficulty difficulty) { 378  return RLP.encodeBlockDifficulty(difficulty); 379  } 380  381  // Warning: This method does not use the object's attributes 382  public static byte[] getUnclesEncodedEx(List<BlockHeader> uncleList) { 383  byte[][] unclesEncoded = new byte[uncleList.size()][]; 384  int i = 0; 385  for (BlockHeader uncle : uncleList) { 386  unclesEncoded[i] = uncle.getFullEncoded(); 387  ++i; 388  } 389  return RLP.encodeList(unclesEncoded); 390  } 391  392  public boolean hasMiningFields() { 393  if (this.bitcoinMergedMiningCoinbaseTransaction != null && this.bitcoinMergedMiningCoinbaseTransaction.length > 0) { 394  return true; 395  } 396  397  if (this.bitcoinMergedMiningHeader != null && this.bitcoinMergedMiningHeader.length > 0) { 398  return true; 399  } 400  401  if (this.bitcoinMergedMiningMerkleProof != null && this.bitcoinMergedMiningMerkleProof.length > 0) { 402  return true; 403  } 404  405  return false; 406  } 407  408  public static byte[] getUnclesEncoded(List<BlockHeader> uncleList) { 409  byte[][] unclesEncoded = new byte[uncleList.size()][]; 410  int i = 0; 411  for (BlockHeader uncle : uncleList) { 412  unclesEncoded[i] = uncle.getFullEncoded(); 413  ++i; 414  } 415  return RLP.encodeList(unclesEncoded); 416  } 417  418  public String toString() { 419  return toStringWithSuffix("\n"); 420  } 421  422  private String toStringWithSuffix(final String suffix) { 423  StringBuilder toStringBuff = new StringBuilder(); 424  toStringBuff.append(" parentHash=").append(toHexStringOrEmpty(parentHash)).append(suffix); 425  toStringBuff.append(" unclesHash=").append(toHexStringOrEmpty(unclesHash)).append(suffix); 426  toStringBuff.append(" coinbase=").append(coinbase).append(suffix); 427  toStringBuff.append(" stateRoot=").append(toHexStringOrEmpty(stateRoot)).append(suffix); 428  toStringBuff.append(" txTrieHash=").append(toHexStringOrEmpty(txTrieRoot)).append(suffix); 429  toStringBuff.append(" receiptsTrieHash=").append(toHexStringOrEmpty(receiptTrieRoot)).append(suffix); 430  toStringBuff.append(" difficulty=").append(difficulty).append(suffix); 431  toStringBuff.append(" number=").append(number).append(suffix); 432  toStringBuff.append(" gasLimit=").append(toHexStringOrEmpty(gasLimit)).append(suffix); 433  toStringBuff.append(" gasUsed=").append(gasUsed).append(suffix); 434  toStringBuff.append(" timestamp=").append(timestamp).append(" (").append(Utils.longToDateTime(timestamp)).append(")").append(suffix); 435  toStringBuff.append(" extraData=").append(toHexStringOrEmpty(extraData)).append(suffix); 436  toStringBuff.append(" minGasPrice=").append(minimumGasPrice).append(suffix); 437  438  return toStringBuff.toString(); 439  } 440  441  public String toFlatString() { 442  return toStringWithSuffix(""); 443  } 444  445  public byte[] getBitcoinMergedMiningHeader() { 446  return bitcoinMergedMiningHeader; 447  } 448  449  public void setBitcoinMergedMiningHeader(byte[] bitcoinMergedMiningHeader) { 450  /* A sealed block header is immutable, cannot be changed */ 451  if (this.sealed) { 452  throw new SealedBlockHeaderException("trying to alter bitcoin merged mining header"); 453  } 454  455  this.bitcoinMergedMiningHeader = bitcoinMergedMiningHeader; 456  } 457  458  public byte[] getBitcoinMergedMiningMerkleProof() { 459  return bitcoinMergedMiningMerkleProof; 460  } 461  462  public void setBitcoinMergedMiningMerkleProof(byte[] bitcoinMergedMiningMerkleProof) { 463  /* A sealed block header is immutable, cannot be changed */ 464  if (this.sealed) { 465  throw new SealedBlockHeaderException("trying to alter bitcoin merged mining merkle proof"); 466  } 467  468  this.bitcoinMergedMiningMerkleProof = bitcoinMergedMiningMerkleProof; 469  } 470  471  public byte[] getBitcoinMergedMiningCoinbaseTransaction() { 472  return bitcoinMergedMiningCoinbaseTransaction; 473  } 474  475  public void setBitcoinMergedMiningCoinbaseTransaction(byte[] bitcoinMergedMiningCoinbaseTransaction) { 476  /* A sealed block header is immutable, cannot be changed */ 477  if (this.sealed) { 478  throw new SealedBlockHeaderException("trying to alter bitcoin merged mining coinbase transaction"); 479  } 480  481  this.bitcoinMergedMiningCoinbaseTransaction = bitcoinMergedMiningCoinbaseTransaction; 482  } 483  484  public String getPrintableHashForMergedMining() { 485  return HashUtil.toPrintableHash(getHashForMergedMining()); 486  } 487  488  public boolean isUMMBlock() { 489  return this.ummRoot != null && this.ummRoot.length != 0; 490  } 491  492  public byte[] getHashForMergedMining() { 493  byte[] hashForMergedMining = this.getBaseHashForMergedMining(); 494  495  if (includeForkDetectionData) { 496  byte[] mergedMiningForkDetectionData = hasMiningFields() ? 497  getMiningForkDetectionData() : 498  miningForkDetectionData; 499  arraycopy( 500  mergedMiningForkDetectionData, 501  0, 502  hashForMergedMining, 503  HASH_FOR_MERGED_MINING_PREFIX_LENGTH, 504  FORK_DETECTION_DATA_LENGTH 505  ); 506  } 507  508  return hashForMergedMining; 509  } 510  511  private byte[] getHashRootForMergedMining(byte[] leftHash) { 512  if (ummRoot.length != UMM_LEAVES_LENGTH){ 513  throw new IllegalStateException( 514  String.format("UMM Root length must be either 0 or 20. Found: %d", ummRoot.length) 515  ); 516  } 517  518  byte[] leftRight = Arrays.copyOf(leftHash, leftHash.length + ummRoot.length); 519  arraycopy(ummRoot, 0, leftRight, leftHash.length, ummRoot.length); 520  521  byte[] root256 = HashUtil.keccak256(leftRight); 522  return root256; 523  } 524  525  public String getPrintableHash() { 526  return HashUtil.toPrintableHash(getHash().getBytes()); 527  } 528  529  public String getParentPrintableHash() { 530  return HashUtil.toPrintableHash(getParentHash().getBytes()); 531  } 532  533  public byte[] getMiningForkDetectionData() { 534  if(includeForkDetectionData) { 535  if (hasMiningFields() && miningForkDetectionData.length == 0) { 536  byte[] hashForMergedMining = getBaseHashForMergedMining(); 537  538  byte[] coinbaseTransaction = getBitcoinMergedMiningCoinbaseTransaction(); 539  540  byte[] mergeMiningTagPrefix = Arrays.copyOf(RskMiningConstants.RSK_TAG, RskMiningConstants.RSK_TAG.length + HASH_FOR_MERGED_MINING_PREFIX_LENGTH); 541  arraycopy(hashForMergedMining, 0, mergeMiningTagPrefix, RskMiningConstants.RSK_TAG.length, HASH_FOR_MERGED_MINING_PREFIX_LENGTH); 542  543  int position = ListArrayUtil.lastIndexOfSubList(coinbaseTransaction, mergeMiningTagPrefix); 544  if (position == -1) { 545  throw new IllegalStateException( 546  String.format("Mining fork detection data could not be found. Header: %s", getPrintableHash()) 547  ); 548  } 549  550  int from = position + RskMiningConstants.RSK_TAG.length + HASH_FOR_MERGED_MINING_PREFIX_LENGTH; 551  int to = from + FORK_DETECTION_DATA_LENGTH; 552  553  if (coinbaseTransaction.length < to) { 554  throw new IllegalStateException( 555  String.format( 556  "Invalid fork detection data length. Expected: %d. Got: %d. Header: %s", 557  FORK_DETECTION_DATA_LENGTH, 558  coinbaseTransaction.length - from, 559  getPrintableHash() 560  ) 561  ); 562  } 563  564  miningForkDetectionData = Arrays.copyOfRange(coinbaseTransaction, from, to); 565  } 566  567  return Arrays.copyOf(miningForkDetectionData, miningForkDetectionData.length); 568  } 569  570  return new byte[0]; 571  } 572  573  /** 574  * Compute the base hash for merged mining, taking into account whether the block is a umm block. 575  * This base hash is later modified to include the forkdetectiondata in its last 12 bytes 576  * 577  * @return The computed hash for merged mining 578  */ 579  private byte[] getBaseHashForMergedMining() { 580  byte[] encodedBlock = getEncoded(false, false); 581  byte[] hashForMergedMining = HashUtil.keccak256(encodedBlock); 582  583  if (isUMMBlock()) { 584  byte[] leftHash = Arrays.copyOfRange(hashForMergedMining, 0, UMM_LEAVES_LENGTH); 585  hashForMergedMining = getHashRootForMergedMining(leftHash); 586  } 587  588  return hashForMergedMining; 589  } 590  591  public boolean isParentOf(BlockHeader header) { 592  return this.getHash().equals(header.getParentHash()); 593  } 594  595  public byte[] getUmmRoot() { 596  return ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; 597  } 598 }