/* * Copyright (c) [2016] [ <ether.camp> ] * This file is part of the ethereumJ library. * * The ethereumJ library 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. * * The ethereumJ library 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 the ethereumJ library. If not, see <http://www.gnu.org/licenses/>. */ package org.ethereum.core; import org.ethereum.config.BlockchainNetConfig; import org.ethereum.crypto.HashUtil; import org.ethereum.util.FastByteComparisons; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; import org.ethereum.util.Utils; import org.spongycastle.util.Arrays; import org.spongycastle.util.BigIntegers; import org.spongycastle.util.encoders.Hex; import java.math.BigInteger; import java.util.List; import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH; import static org.ethereum.util.ByteUtil.toHexString; /** * Block header is a value object containing * the basic information of a block */ public class BlockHeader { public static final int NONCE_LENGTH = 8; public static final int HASH_LENGTH = 32; public static final int ADDRESS_LENGTH = 20; public static final int MAX_HEADER_SIZE = 592; /* The SHA3 256-bit hash of the parent block, in its entirety */ private byte[] parentHash; /* The SHA3 256-bit hash of the uncles list portion of this block */ private byte[] unclesHash; /* The 160-bit address to which all fees collected from the * successful mining of this block be transferred; formally */ private byte[] coinbase; /* The SHA3 256-bit hash of the root node of the state trie, * after all transactions are executed and finalisations applied */ private byte[] stateRoot; /* The SHA3 256-bit hash of the root node of the trie structure * populated with each transaction in the transaction * list portion, the trie is populate by [key, val] --> [rlp(index), rlp(tx_recipe)] * of the block */ private byte[] txTrieRoot; /* The SHA3 256-bit hash of the root node of the trie structure * populated with each transaction recipe in the transaction recipes * list portion, the trie is populate by [key, val] --> [rlp(index), rlp(tx_recipe)] * of the block */ private byte[] receiptTrieRoot; /*todo: comment it when you know what the fuck it is*/ private byte[] logsBloom; /* A scalar value corresponding to the difficulty level of this block. * This can be calculated from the previous block’s difficulty level * and the timestamp */ private byte[] difficulty; /* A scalar value equal to the reasonable output of Unix's time() * at this block's inception */ private long timestamp; /* A scalar value equal to the number of ancestor blocks. * The genesis block has a number of zero */ private long number; /* A scalar value equal to the current limit of gas expenditure per block */ private byte[] gasLimit; /* A scalar value equal to the total gas used in transactions in this block */ private long gasUsed; private byte[] mixHash; /* An arbitrary byte array containing data relevant to this block. * With the exception of the genesis block, this must be 32 bytes or fewer */ private byte[] extraData; /* A 256-bit hash which proves that a sufficient amount * of computation has been carried out on this block */ private byte[] nonce; private byte[] hashCache; public BlockHeader(byte[] encoded) { this((RLPList) RLP.decode2(encoded).get(0)); } public BlockHeader(RLPList rlpHeader) { this.parentHash = rlpHeader.get(0).getRLPData(); this.unclesHash = rlpHeader.get(1).getRLPData(); this.coinbase = rlpHeader.get(2).getRLPData(); this.stateRoot = rlpHeader.get(3).getRLPData(); this.txTrieRoot = rlpHeader.get(4).getRLPData(); if (this.txTrieRoot == null) this.txTrieRoot = EMPTY_TRIE_HASH; this.receiptTrieRoot = rlpHeader.get(5).getRLPData(); if (this.receiptTrieRoot == null) this.receiptTrieRoot = EMPTY_TRIE_HASH; this.logsBloom = rlpHeader.get(6).getRLPData(); this.difficulty = rlpHeader.get(7).getRLPData(); byte[] nrBytes = rlpHeader.get(8).getRLPData(); byte[] glBytes = rlpHeader.get(9).getRLPData(); byte[] guBytes = rlpHeader.get(10).getRLPData(); byte[] tsBytes = rlpHeader.get(11).getRLPData(); this.number = nrBytes == null ? 0 : (new BigInteger(1, nrBytes)).longValue(); this.gasLimit = glBytes; this.gasUsed = guBytes == null ? 0 : (new BigInteger(1, guBytes)).longValue(); this.timestamp = tsBytes == null ? 0 : (new BigInteger(1, tsBytes)).longValue(); this.extraData = rlpHeader.get(12).getRLPData(); this.mixHash = rlpHeader.get(13).getRLPData(); this.nonce = rlpHeader.get(14).getRLPData(); } public BlockHeader(byte[] parentHash, byte[] unclesHash, byte[] coinbase, byte[] logsBloom, byte[] difficulty, long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, byte[] mixHash, byte[] nonce) { this.parentHash = parentHash; this.unclesHash = unclesHash; this.coinbase = coinbase; this.logsBloom = logsBloom; this.difficulty = difficulty; this.number = number; this.gasLimit = gasLimit; this.gasUsed = gasUsed; this.timestamp = timestamp; this.extraData = extraData; this.mixHash = mixHash; this.nonce = nonce; this.stateRoot = HashUtil.EMPTY_TRIE_HASH; } public boolean isGenesis() { return this.getNumber() == Genesis.NUMBER; } public byte[] getParentHash() { return parentHash; } public byte[] getUnclesHash() { return unclesHash; } public void setUnclesHash(byte[] unclesHash) { this.unclesHash = unclesHash; hashCache = null; } public byte[] getCoinbase() { return coinbase; } public void setCoinbase(byte[] coinbase) { this.coinbase = coinbase; hashCache = null; } public byte[] getStateRoot() { return stateRoot; } public void setStateRoot(byte[] stateRoot) { this.stateRoot = stateRoot; hashCache = null; } public byte[] getTxTrieRoot() { return txTrieRoot; } public void setReceiptsRoot(byte[] receiptTrieRoot) { this.receiptTrieRoot = receiptTrieRoot; hashCache = null; } public byte[] getReceiptsRoot() { return receiptTrieRoot; } public void setTransactionsRoot(byte[] stateRoot) { this.txTrieRoot = stateRoot; hashCache = null; } public byte[] getLogsBloom() { return logsBloom; } public byte[] getDifficulty() { return difficulty; } public BigInteger getDifficultyBI() { return new BigInteger(1, difficulty); } public void setDifficulty(byte[] difficulty) { this.difficulty = difficulty; hashCache = null; } public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; hashCache = null; } public long getNumber() { return number; } public void setNumber(long number) { this.number = number; hashCache = null; } public byte[] getGasLimit() { return gasLimit; } public void setGasLimit(byte[] gasLimit) { this.gasLimit = gasLimit; hashCache = null; } public long getGasUsed() { return gasUsed; } public void setGasUsed(long gasUsed) { this.gasUsed = gasUsed; hashCache = null; } public byte[] getMixHash() { return mixHash; } public void setMixHash(byte[] mixHash) { this.mixHash = mixHash; hashCache = null; } public byte[] getExtraData() { return extraData; } public byte[] getNonce() { return nonce; } public void setNonce(byte[] nonce) { this.nonce = nonce; hashCache = null; } public void setLogsBloom(byte[] logsBloom) { this.logsBloom = logsBloom; hashCache = null; } public void setExtraData(byte[] extraData) { this.extraData = extraData; hashCache = null; } public byte[] getHash() { if (hashCache == null) { hashCache = HashUtil.sha3(getEncoded()); } return hashCache; } public byte[] getEncoded() { return this.getEncoded(true); // with nonce } public byte[] getEncodedWithoutNonce() { return this.getEncoded(false); } public byte[] getEncoded(boolean withNonce) { byte[] parentHash = RLP.encodeElement(this.parentHash); byte[] unclesHash = RLP.encodeElement(this.unclesHash); byte[] coinbase = RLP.encodeElement(this.coinbase); byte[] stateRoot = RLP.encodeElement(this.stateRoot); if (txTrieRoot == null) this.txTrieRoot = EMPTY_TRIE_HASH; byte[] txTrieRoot = RLP.encodeElement(this.txTrieRoot); if (receiptTrieRoot == null) this.receiptTrieRoot = EMPTY_TRIE_HASH; byte[] receiptTrieRoot = RLP.encodeElement(this.receiptTrieRoot); byte[] logsBloom = RLP.encodeElement(this.logsBloom); byte[] difficulty = RLP.encodeBigInteger(new BigInteger(1, this.difficulty)); byte[] number = RLP.encodeBigInteger(BigInteger.valueOf(this.number)); byte[] gasLimit = RLP.encodeElement(this.gasLimit); byte[] gasUsed = RLP.encodeBigInteger(BigInteger.valueOf(this.gasUsed)); byte[] timestamp = RLP.encodeBigInteger(BigInteger.valueOf(this.timestamp)); byte[] extraData = RLP.encodeElement(this.extraData); if (withNonce) { byte[] mixHash = RLP.encodeElement(this.mixHash); byte[] nonce = RLP.encodeElement(this.nonce); return RLP.encodeList(parentHash, unclesHash, coinbase, stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number, gasLimit, gasUsed, timestamp, extraData, mixHash, nonce); } else { return RLP.encodeList(parentHash, unclesHash, coinbase, stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number, gasLimit, gasUsed, timestamp, extraData); } } public byte[] getUnclesEncoded(List<BlockHeader> uncleList) { byte[][] unclesEncoded = new byte[uncleList.size()][]; int i = 0; for (BlockHeader uncle : uncleList) { unclesEncoded[i] = uncle.getEncoded(); ++i; } return RLP.encodeList(unclesEncoded); } public byte[] getPowBoundary() { return BigIntegers.asUnsignedByteArray(32, BigInteger.ONE.shiftLeft(256).divide(getDifficultyBI())); } public byte[] calcPowValue() { // nonce bytes are expected in Little Endian order, reverting byte[] nonceReverted = Arrays.reverse(nonce); byte[] hashWithoutNonce = HashUtil.sha3(getEncodedWithoutNonce()); byte[] seed = Arrays.concatenate(hashWithoutNonce, nonceReverted); byte[] seedHash = HashUtil.sha512(seed); byte[] concat = Arrays.concatenate(seedHash, mixHash); return HashUtil.sha3(concat); } public BigInteger calcDifficulty(BlockchainNetConfig config, BlockHeader parent) { return config.getConfigForBlock(getNumber()). calcDifficulty(this, parent); } public String toString() { return toStringWithSuffix("\n"); } private String toStringWithSuffix(final String suffix) { StringBuilder toStringBuff = new StringBuilder(); toStringBuff.append(" hash=").append(toHexString(getHash())).append(suffix); toStringBuff.append(" parentHash=").append(toHexString(parentHash)).append(suffix); toStringBuff.append(" unclesHash=").append(toHexString(unclesHash)).append(suffix); toStringBuff.append(" coinbase=").append(toHexString(coinbase)).append(suffix); toStringBuff.append(" stateRoot=").append(toHexString(stateRoot)).append(suffix); toStringBuff.append(" txTrieHash=").append(toHexString(txTrieRoot)).append(suffix); toStringBuff.append(" receiptsTrieHash=").append(toHexString(receiptTrieRoot)).append(suffix); toStringBuff.append(" difficulty=").append(toHexString(difficulty)).append(suffix); toStringBuff.append(" number=").append(number).append(suffix); // toStringBuff.append(" gasLimit=").append(gasLimit).append(suffix); toStringBuff.append(" gasLimit=").append(toHexString(gasLimit)).append(suffix); toStringBuff.append(" gasUsed=").append(gasUsed).append(suffix); toStringBuff.append(" timestamp=").append(timestamp).append(" (").append(Utils.longToDateTime(timestamp)).append(")").append(suffix); toStringBuff.append(" extraData=").append(toHexString(extraData)).append(suffix); toStringBuff.append(" mixHash=").append(toHexString(mixHash)).append(suffix); toStringBuff.append(" nonce=").append(toHexString(nonce)).append(suffix); return toStringBuff.toString(); } public String toFlatString() { return toStringWithSuffix(""); } public String getShortDescr() { return "#" + getNumber() + " (" + Hex.toHexString(getHash()).substring(0,6) + " <~ " + Hex.toHexString(getParentHash()).substring(0,6) + ")"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BlockHeader that = (BlockHeader) o; return FastByteComparisons.equal(getHash(), that.getHash()); } @Override public int hashCode() { return Arrays.hashCode(getHash()); } }