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 }