Nick Savers brilliant work on Miner:
+ the option to vaildate/mine blocks + nonce validation + calc for max gas for a block + difficulty calc
This commit is contained in:
parent
708428c01e
commit
739b1425ef
|
@ -4,11 +4,13 @@ import org.ethereum.crypto.HashUtil;
|
||||||
import org.ethereum.manager.WorldManager;
|
import org.ethereum.manager.WorldManager;
|
||||||
import org.ethereum.trie.Trie;
|
import org.ethereum.trie.Trie;
|
||||||
import org.ethereum.util.ByteUtil;
|
import org.ethereum.util.ByteUtil;
|
||||||
|
import org.ethereum.util.FastByteComparisons;
|
||||||
import org.ethereum.util.RLP;
|
import org.ethereum.util.RLP;
|
||||||
import org.ethereum.util.RLPElement;
|
import org.ethereum.util.RLPElement;
|
||||||
import org.ethereum.util.RLPList;
|
import org.ethereum.util.RLPList;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.spongycastle.util.Arrays;
|
||||||
import org.spongycastle.util.BigIntegers;
|
import org.spongycastle.util.BigIntegers;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
|
@ -104,8 +106,9 @@ public class Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block getParent() {
|
public Block getParent() {
|
||||||
// TODO retrieve Parent from chain
|
byte[] rlpEncoded = WorldManager.instance.chainDB.get(ByteUtil
|
||||||
return null;
|
.longToBytes(this.getNumber() - 1));
|
||||||
|
return new Block(rlpEncoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getParentHash() {
|
public byte[] getParentHash() {
|
||||||
|
@ -128,6 +131,11 @@ public class Block {
|
||||||
return this.header.getStateRoot();
|
return this.header.getStateRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setStateRoot(byte[] stateRoot) {
|
||||||
|
if (!parsed) parseRLP();
|
||||||
|
this.header.setStateRoot(stateRoot);
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getTxTrieRoot() {
|
public byte[] getTxTrieRoot() {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
return this.header.getTxTrieRoot();
|
return this.header.getTxTrieRoot();
|
||||||
|
@ -154,7 +162,7 @@ public class Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGenesis() {
|
public boolean isGenesis() {
|
||||||
return this.getNumber() == 0;
|
return this.getNumber() == Genesis.NUMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getGasLimit() {
|
public long getGasLimit() {
|
||||||
|
@ -177,6 +185,11 @@ public class Block {
|
||||||
return this.header.getNonce();
|
return this.header.getNonce();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNonce(byte[] nonce) {
|
||||||
|
this.header.setNonce(nonce);
|
||||||
|
rlpEncoded = null;
|
||||||
|
}
|
||||||
|
|
||||||
public Trie getTxsState() {
|
public Trie getTxsState() {
|
||||||
return this.txsState;
|
return this.txsState;
|
||||||
}
|
}
|
||||||
|
@ -304,13 +317,20 @@ public class Block {
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
boolean isValid = true;
|
boolean isValid = true;
|
||||||
|
|
||||||
|
if(!this.isGenesis()) {
|
||||||
// verify difficulty meets requirements
|
// verify difficulty meets requirements
|
||||||
//isValid = this.getDifficulty() == this.calcDifficulty();
|
isValid = this.getDifficulty() == this.calcDifficulty();
|
||||||
|
// verify nonce meest difficulty requirements
|
||||||
|
isValid = this.validateNonce();
|
||||||
// verify gasLimit meets requirements
|
// verify gasLimit meets requirements
|
||||||
//isValid = this.getGasLimit() == this.calcGasLimit();
|
isValid = this.getGasLimit() == this.calcGasLimit();
|
||||||
// verify timestamp meets requirements
|
// verify timestamp meets requirements
|
||||||
//isValid = this.getTimestamp() > this.getParent().getTimestamp();
|
isValid = this.getTimestamp() > this.getParent().getTimestamp();
|
||||||
|
// verify extraData doesn't exceed 1024 bytes
|
||||||
|
isValid = this.getExtraData() == null || this.getExtraData().length <= 1024;
|
||||||
|
}
|
||||||
|
if(!isValid)
|
||||||
|
logger.warn("!!!Invalid block!!!");
|
||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +344,7 @@ public class Block {
|
||||||
return Genesis.GAS_LIMIT;
|
return Genesis.GAS_LIMIT;
|
||||||
else {
|
else {
|
||||||
Block parent = this.getParent();
|
Block parent = this.getParent();
|
||||||
return Math.max(MIN_GAS_LIMIT, (parent.header.getGasLimit() * (1024 - 1) + (parent.header.getGasUsed() * 6 / 5)) / 1024);
|
return Math.max(MIN_GAS_LIMIT, (parent.getGasLimit() * (1024 - 1) + (parent.getGasUsed() * 6 / 5)) / 1024);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,17 +364,26 @@ public class Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that block is valid for its difficulty
|
||||||
|
*
|
||||||
|
* @param block
|
||||||
|
* @param difficulty
|
||||||
|
* @param testNonce
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean validateNonce() {
|
||||||
|
BigInteger max = BigInteger.valueOf(2).pow(256);
|
||||||
|
byte[] target = BigIntegers.asUnsignedByteArray(32,
|
||||||
|
max.divide(new BigInteger(1, this.getDifficulty())));
|
||||||
|
byte[] hash = HashUtil.sha3(this.getEncodedWithoutNonce());
|
||||||
|
byte[] concat = Arrays.concatenate(hash, this.getNonce());
|
||||||
|
byte[] result = HashUtil.sha3(concat);
|
||||||
|
return FastByteComparisons.compareTo(result, 0, 32, target, 0, 32) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
if(rlpEncoded == null) {
|
if(rlpEncoded == null) {
|
||||||
|
|
||||||
// TODO: Alternative clean way to encode, using RLP.encode() after it's optimized
|
|
||||||
// Object[] header = new Object[] { parentHash, unclesHash, coinbase,
|
|
||||||
// stateRoot, txTrieRoot, difficulty, number, minGasPrice,
|
|
||||||
// gasLimit, gasUsed, timestamp, extraData, nonce };
|
|
||||||
// Object[] transactions = this.getTransactionsList().toArray();
|
|
||||||
// Object[] uncles = this.getUncleList().toArray();
|
|
||||||
// return RLP.encode(new Object[] { header, transactions, uncles });
|
|
||||||
|
|
||||||
byte[] header = this.header.getEncoded();
|
byte[] header = this.header.getEncoded();
|
||||||
byte[] transactions = RLP.encodeList();
|
byte[] transactions = RLP.encodeList();
|
||||||
byte[] uncles = RLP.encodeList();
|
byte[] uncles = RLP.encodeList();
|
||||||
|
@ -363,4 +392,13 @@ public class Block {
|
||||||
}
|
}
|
||||||
return rlpEncoded;
|
return rlpEncoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getEncodedWithoutNonce() {
|
||||||
|
if (!parsed) parseRLP();
|
||||||
|
byte[] header = this.header.getEncodedWithoutNonce();
|
||||||
|
byte[] transactions = RLP.encodeList();
|
||||||
|
byte[] uncles = RLP.encodeList();
|
||||||
|
|
||||||
|
return RLP.encodeList(header, transactions, uncles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,6 +174,14 @@ public class BlockHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getEncoded() {
|
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[] parentHash = RLP.encodeElement(this.parentHash);
|
||||||
byte[] unclesHash = RLP.encodeElement(this.unclesHash);
|
byte[] unclesHash = RLP.encodeElement(this.unclesHash);
|
||||||
byte[] coinbase = RLP.encodeElement(this.coinbase);
|
byte[] coinbase = RLP.encodeElement(this.coinbase);
|
||||||
|
@ -186,11 +194,16 @@ public class BlockHeader {
|
||||||
byte[] gasUsed = RLP.encodeBigInteger(BigInteger.valueOf(this.gasUsed));
|
byte[] gasUsed = RLP.encodeBigInteger(BigInteger.valueOf(this.gasUsed));
|
||||||
byte[] timestamp = RLP.encodeBigInteger(BigInteger.valueOf(this.timestamp));
|
byte[] timestamp = RLP.encodeBigInteger(BigInteger.valueOf(this.timestamp));
|
||||||
byte[] extraData = RLP.encodeElement(this.extraData);
|
byte[] extraData = RLP.encodeElement(this.extraData);
|
||||||
|
if(withNonce) {
|
||||||
byte[] nonce = RLP.encodeElement(this.nonce);
|
byte[] nonce = RLP.encodeElement(this.nonce);
|
||||||
|
|
||||||
return RLP.encodeList(parentHash, unclesHash, coinbase,
|
return RLP.encodeList(parentHash, unclesHash, coinbase,
|
||||||
stateRoot, txTrieRoot, difficulty, number,
|
stateRoot, txTrieRoot, difficulty, number,
|
||||||
minGasPrice, gasLimit, gasUsed, timestamp, extraData, nonce);
|
minGasPrice, gasLimit, gasUsed, timestamp, extraData, nonce);
|
||||||
|
} else {
|
||||||
|
return RLP.encodeList(parentHash, unclesHash, coinbase,
|
||||||
|
stateRoot, txTrieRoot, difficulty, number,
|
||||||
|
minGasPrice, gasLimit, gasUsed, timestamp, extraData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private StringBuffer toStringBuff = new StringBuffer();
|
private StringBuffer toStringBuff = new StringBuffer();
|
||||||
|
@ -201,7 +214,7 @@ public class BlockHeader {
|
||||||
toStringBuff.append(" parentHash=" + ByteUtil.toHexString(parentHash)).append("\n");
|
toStringBuff.append(" parentHash=" + ByteUtil.toHexString(parentHash)).append("\n");
|
||||||
toStringBuff.append(" unclesHash=" + ByteUtil.toHexString(unclesHash)).append("\n");
|
toStringBuff.append(" unclesHash=" + ByteUtil.toHexString(unclesHash)).append("\n");
|
||||||
toStringBuff.append(" coinbase=" + ByteUtil.toHexString(coinbase)).append("\n");
|
toStringBuff.append(" coinbase=" + ByteUtil.toHexString(coinbase)).append("\n");
|
||||||
toStringBuff.append(" stateHash=" + ByteUtil.toHexString(stateRoot)).append("\n");
|
toStringBuff.append(" stateRoot=" + ByteUtil.toHexString(stateRoot)).append("\n");
|
||||||
toStringBuff.append(" txTrieHash=" + ByteUtil.toHexString(txTrieRoot)).append("\n");
|
toStringBuff.append(" txTrieHash=" + ByteUtil.toHexString(txTrieRoot)).append("\n");
|
||||||
toStringBuff.append(" difficulty=" + ByteUtil.toHexString(difficulty)).append("\n");
|
toStringBuff.append(" difficulty=" + ByteUtil.toHexString(difficulty)).append("\n");
|
||||||
toStringBuff.append(" number=" + number).append("\n");
|
toStringBuff.append(" number=" + number).append("\n");
|
||||||
|
@ -218,7 +231,7 @@ public class BlockHeader {
|
||||||
toStringBuff.append(" parentHash=" + ByteUtil.toHexString(parentHash)).append("");
|
toStringBuff.append(" parentHash=" + ByteUtil.toHexString(parentHash)).append("");
|
||||||
toStringBuff.append(" unclesHash=" + ByteUtil.toHexString(unclesHash)).append("");
|
toStringBuff.append(" unclesHash=" + ByteUtil.toHexString(unclesHash)).append("");
|
||||||
toStringBuff.append(" coinbase=" + ByteUtil.toHexString(coinbase)).append("");
|
toStringBuff.append(" coinbase=" + ByteUtil.toHexString(coinbase)).append("");
|
||||||
toStringBuff.append(" stateHash=" + ByteUtil.toHexString(stateRoot)).append("");
|
toStringBuff.append(" stateRoot=" + ByteUtil.toHexString(stateRoot)).append("");
|
||||||
toStringBuff.append(" txTrieHash=" + ByteUtil.toHexString(txTrieRoot)).append("");
|
toStringBuff.append(" txTrieHash=" + ByteUtil.toHexString(txTrieRoot)).append("");
|
||||||
toStringBuff.append(" difficulty=" + ByteUtil.toHexString(difficulty)).append("");
|
toStringBuff.append(" difficulty=" + ByteUtil.toHexString(difficulty)).append("");
|
||||||
toStringBuff.append(" number=" + number).append("");
|
toStringBuff.append(" number=" + number).append("");
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package org.ethereum.core;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import org.ethereum.crypto.HashUtil;
|
||||||
|
import org.ethereum.util.ByteUtil;
|
||||||
|
import org.ethereum.util.FastByteComparisons;
|
||||||
|
import org.spongycastle.util.Arrays;
|
||||||
|
import org.spongycastle.util.BigIntegers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Miner performs the proof-of-work needed for a valid block
|
||||||
|
*
|
||||||
|
* The mining proof-of-work (PoW) exists as a cryptographically secure nonce
|
||||||
|
* that proves beyond reasonable doubt that a particular amount of computation
|
||||||
|
* has been expended in the determination of some token value n.
|
||||||
|
* It is utilised to enforce the blockchain security by giving meaning
|
||||||
|
* and credence to the notion of difficulty (and, by extension, total difficulty).
|
||||||
|
*
|
||||||
|
* However, since mining new blocks comes with an attached reward,
|
||||||
|
* the proof-of-work not only functions as a method of securing confidence
|
||||||
|
* that the blockchain will remain canonical into the future, but also as
|
||||||
|
* a wealth distribution mechanism.
|
||||||
|
*
|
||||||
|
* See Yellow Paper: http://www.gavwood.com/Paper.pdf (chapter 11.5 Mining Proof-of-Work)
|
||||||
|
*/
|
||||||
|
public class Miner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a nonce to given block which complies with the given difficulty
|
||||||
|
*
|
||||||
|
* For the PoC series, we use a simplified proof-of-work.
|
||||||
|
* This is not ASIC resistant and is meant merely as a placeholder.
|
||||||
|
* It utilizes the bare SHA3 hash function to secure the block chain by requiring
|
||||||
|
* the SHA3 hash of the concatenation of the nonce and the header’s SHA3 hash to be
|
||||||
|
* sufficiently low. It is formally defined as PoW:
|
||||||
|
*
|
||||||
|
* PoW(H, n) ≡ BE(SHA3(SHA3(RLP(H!n)) ◦ n))
|
||||||
|
*
|
||||||
|
* where:
|
||||||
|
* RLP(H!n) is the RLP encoding of the block header H, not including the
|
||||||
|
* final nonce component;
|
||||||
|
* SHA3 is the SHA3 hash function accepting an arbitrary length series of
|
||||||
|
* bytes and evaluating to a series of 32 bytes (i.e. 256-bit);
|
||||||
|
* n is the nonce, a series of 32 bytes;
|
||||||
|
* o is the series concatenation operator;
|
||||||
|
* BE(X) evaluates to the value equal to X when interpreted as a
|
||||||
|
* big-endian-encoded integer.
|
||||||
|
*
|
||||||
|
* @param newBlock without a valid nonce
|
||||||
|
* @param difficulty - the mining difficulty
|
||||||
|
* @return true if valid nonce has been added to the block
|
||||||
|
*/
|
||||||
|
public boolean mine(Block newBlock, byte[] difficulty) {
|
||||||
|
|
||||||
|
BigInteger max = BigInteger.valueOf(2).pow(256);
|
||||||
|
byte[] target = BigIntegers.asUnsignedByteArray(32,
|
||||||
|
max.divide(new BigInteger(1, difficulty)));
|
||||||
|
|
||||||
|
byte[] hash = HashUtil.sha3(newBlock.getEncodedWithoutNonce());
|
||||||
|
byte[] testNonce = new byte[32];
|
||||||
|
byte[] concat;
|
||||||
|
|
||||||
|
while(ByteUtil.increment(testNonce)) {
|
||||||
|
concat = Arrays.concatenate(hash, testNonce);
|
||||||
|
byte[] result = HashUtil.sha3(concat);
|
||||||
|
if(FastByteComparisons.compareTo(result, 0, 32, target, 0, 32) < 0) {
|
||||||
|
newBlock.setNonce(testNonce);
|
||||||
|
// System.out.println(Hex.toHexString(newBlock.getEncoded()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false; // couldn't find a valid nonce
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package org.ethereum.util;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
@ -37,6 +38,10 @@ public class ByteUtil {
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] longToBytes(long l) {
|
||||||
|
return ByteBuffer.allocate(8).putLong(l).array();
|
||||||
|
}
|
||||||
|
|
||||||
public static String toHexString(byte[] data){
|
public static String toHexString(byte[] data){
|
||||||
if (data == null) return "null";
|
if (data == null) return "null";
|
||||||
else return Hex.toHexString(data);
|
else return Hex.toHexString(data);
|
||||||
|
@ -155,6 +160,16 @@ public class ByteUtil {
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static byte[] stripLeadingZeroes(byte[] data){
|
public static byte[] stripLeadingZeroes(byte[] data){
|
||||||
|
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
|
@ -175,4 +190,19 @@ public class ByteUtil {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* increment byte array as a number until max is reached
|
||||||
|
*/
|
||||||
|
public static boolean increment(byte[] bytes) {
|
||||||
|
final int startIndex = 0;
|
||||||
|
int i;
|
||||||
|
for (i = bytes.length-1; i >= startIndex; i--) {
|
||||||
|
bytes[i]++;
|
||||||
|
if (bytes[i] != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// we return false when all bytes are 0 again
|
||||||
|
return (i >= startIndex || bytes[startIndex] != 0);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,9 @@ package org.ethereum.core;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import org.ethereum.manager.WorldManager;
|
||||||
import org.ethereum.net.message.StaticMessages;
|
import org.ethereum.net.message.StaticMessages;
|
||||||
|
import org.ethereum.util.ByteUtil;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
import org.ethereum.core.Block;
|
import org.ethereum.core.Block;
|
||||||
import org.ethereum.core.Genesis;
|
import org.ethereum.core.Genesis;
|
||||||
|
@ -163,28 +165,40 @@ public class BlockTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalcDifficulty() {
|
public void testCalcDifficulty() {
|
||||||
byte[] diffBytes = Genesis.getInstance().calcDifficulty();
|
Block genesis = Genesis.getInstance();
|
||||||
BigInteger difficulty = new BigInteger(1, diffBytes);
|
BigInteger difficulty = new BigInteger(1, genesis.calcDifficulty());
|
||||||
System.out.println("Genesis difficulty = " + difficulty.toString());
|
System.out.println("Genesis difficulty = " + difficulty.toString());
|
||||||
assertEquals(new BigInteger(1, Genesis.DIFFICULTY), difficulty);
|
assertEquals(new BigInteger(1, Genesis.DIFFICULTY), difficulty);
|
||||||
|
|
||||||
|
// Storing genesis because the parent needs to be in the DB for calculation.
|
||||||
|
WorldManager.instance.chainDB.put(ByteUtil.longToBytes(Genesis.NUMBER),
|
||||||
|
genesis.getEncoded());
|
||||||
|
|
||||||
Block block1 = new Block(Hex.decode(block_1));
|
Block block1 = new Block(Hex.decode(block_1));
|
||||||
diffBytes = block1.calcDifficulty();
|
BigInteger calcDifficulty = new BigInteger(1, block1.calcDifficulty());
|
||||||
difficulty = new BigInteger(1, diffBytes);
|
BigInteger actualDifficulty = new BigInteger(1, block1.getDifficulty());
|
||||||
System.out.println("Block#1 difficulty = " + difficulty.toString());
|
System.out.println("Block#1 actual difficulty = " + actualDifficulty.toString());
|
||||||
assertEquals(new BigInteger(""), difficulty);
|
System.out.println("Block#1 calculated difficulty = " + calcDifficulty.toString());
|
||||||
|
assertEquals(actualDifficulty, calcDifficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalcGasLimit() {
|
public void testCalcGasLimit() {
|
||||||
long gasLimit = Genesis.getInstance().calcGasLimit();
|
Block genesis = Genesis.getInstance();
|
||||||
|
long gasLimit = genesis.calcGasLimit();
|
||||||
System.out.println("Genesis gasLimit = " + gasLimit);
|
System.out.println("Genesis gasLimit = " + gasLimit);
|
||||||
assertEquals(Genesis.GAS_LIMIT, gasLimit);
|
assertEquals(Genesis.GAS_LIMIT, gasLimit);
|
||||||
|
|
||||||
|
// Storing genesis because the parent needs to be in the DB for calculation.
|
||||||
|
WorldManager.instance.chainDB.put(ByteUtil.longToBytes(Genesis.NUMBER),
|
||||||
|
genesis.getEncoded());
|
||||||
|
|
||||||
// Test with block
|
// Test with block
|
||||||
Block block1 = new Block(Hex.decode(block_1));
|
Block block1 = new Block(Hex.decode(block_1));
|
||||||
gasLimit = block1.calcGasLimit();
|
long calcGasLimit = block1.calcGasLimit();
|
||||||
System.out.println("Block#1 gasLimit = " + gasLimit);
|
long actualGasLimit = block1.getGasLimit();
|
||||||
assertEquals(999023, gasLimit);
|
System.out.println("Block#1 actual gasLimit = " + actualGasLimit);
|
||||||
|
System.out.println("Block#1 calculated gasLimit = " + calcGasLimit);
|
||||||
|
assertEquals(actualGasLimit, calcGasLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
package org.ethereum.core;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.ethereum.util.ByteUtil;
|
||||||
|
import org.ethereum.util.FastByteComparisons;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.spongycastle.util.BigIntegers;
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
|
public class MinerTest {
|
||||||
|
|
||||||
|
// Example block#32 from Poc5 chain - rlpEncoded without nonce
|
||||||
|
private String rlpWithoutNonce = "f894f890a00a312c2b0a8f125c60a3976b6e508e740e095eb59943988d9bbfb8"
|
||||||
|
+ "aa43922e31a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e559de5527492bcb42ec68d07df0742a98ec3f1ea050188ab86bdf164ac90eb2835a04a8930aae5393c3a2ef1166fb95028f9456b880833ee248208609184e72a000830eca0080845387fd2080c0c0";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMine() {
|
||||||
|
boolean miningTestEnabled = false;
|
||||||
|
|
||||||
|
if(miningTestEnabled) {
|
||||||
|
Block block = createBlock();
|
||||||
|
assertEquals(rlpWithoutNonce, Hex.toHexString(block.getEncodedWithoutNonce()));
|
||||||
|
System.out.println("Searching for nonce of following block: \n" + block.toString());
|
||||||
|
|
||||||
|
Miner miner = new Miner();
|
||||||
|
boolean mined = miner.mine(block, block.getDifficulty());
|
||||||
|
assertTrue(mined);
|
||||||
|
boolean valid = block.validateNonce();
|
||||||
|
assertTrue(valid);
|
||||||
|
|
||||||
|
// expectedHash is the actual hash from block#32 in PoC5 chain based on nonce below
|
||||||
|
String expectedHash = "ce7201f6cc5eb1a6f35c7dda8acda111647a0f8a5bf0518e46579b03e29fe14b";
|
||||||
|
assertEquals(expectedHash, Hex.toHexString(block.getHash()));
|
||||||
|
|
||||||
|
// expectedNonce is the actual nonce from block#32 in Poc5 chain
|
||||||
|
String expectedNonce = "0000000000000000000000000000000000000000000000001f52ebb192c4ea97"; // from Poc5 chain
|
||||||
|
// Actual is "000000000000000000000000000000000000000000000000000000000098cc15"
|
||||||
|
// but that might also be a valid nonce in compliance with PoW(H!n, n) < (2^256 / difficulty)
|
||||||
|
assertEquals(expectedNonce, Hex.toHexString(block.getNonce()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a block equal to block#32 on PoC5 testnet (protocol 19)
|
||||||
|
* Where nonce was '0000000000000000000000000000000000000000000000001f52ebb192c4ea97'
|
||||||
|
* and resulting hash 'ce7201f6cc5eb1a6f35c7dda8acda111647a0f8a5bf0518e46579b03e29fe14b'
|
||||||
|
*/
|
||||||
|
private Block createBlock() {
|
||||||
|
byte[] parentHash = Hex.decode("0a312c2b0a8f125c60a3976b6e508e740e095eb59943988d9bbfb8aa43922e31");
|
||||||
|
byte[] unclesHash = Hex.decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347");
|
||||||
|
byte[] coinbase = Hex.decode("e559de5527492bcb42ec68d07df0742a98ec3f1e");
|
||||||
|
byte[] difficulty = Hex.decode("3ee248");
|
||||||
|
byte[] nonce = null;
|
||||||
|
long number = 32;
|
||||||
|
long minGasPrice = 10000000000000L;
|
||||||
|
long gasLimit = 969216;
|
||||||
|
long gasUsed = 0;
|
||||||
|
long timestamp = 1401421088;
|
||||||
|
Block newBlock = new Block(parentHash, unclesHash, coinbase,
|
||||||
|
difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp,
|
||||||
|
null, nonce, null, null);
|
||||||
|
// Setting stateRoot manually, because don't have state available.
|
||||||
|
newBlock.setStateRoot(Hex.decode("50188ab86bdf164ac90eb2835a04a8930aae5393c3a2ef1166fb95028f9456b8"));
|
||||||
|
return newBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test shows the difference between iterating over,
|
||||||
|
* and comparing byte[] vs BigInteger value.
|
||||||
|
*
|
||||||
|
* Results indicate that the former has ~15x better performance.
|
||||||
|
* Therefore this is used in the Miner.mine() method.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testIncrementPerformance() {
|
||||||
|
boolean testEnabled = false;
|
||||||
|
|
||||||
|
if(testEnabled) {
|
||||||
|
byte[] counter1 = new byte[4];
|
||||||
|
byte[] max = ByteBuffer.allocate(4).putInt(Integer.MAX_VALUE).array();
|
||||||
|
long start1 = System.currentTimeMillis();
|
||||||
|
while(ByteUtil.increment(counter1)) {
|
||||||
|
if(FastByteComparisons.compareTo(counter1, 0, 4, max, 0, 4) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println(System.currentTimeMillis() - start1 + "ms to reach: " + Hex.toHexString(counter1));
|
||||||
|
|
||||||
|
BigInteger counter2 = BigInteger.ZERO;
|
||||||
|
long start2 = System.currentTimeMillis();
|
||||||
|
while(true) {
|
||||||
|
if(counter2.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter2 = counter2.add(BigInteger.ONE);
|
||||||
|
|
||||||
|
}
|
||||||
|
System.out.println(System.currentTimeMillis() - start2 + "ms to reach: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(4, counter2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue