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.trie.Trie;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.ethereum.util.FastByteComparisons;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.ethereum.util.RLPElement;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.Arrays;
|
||||
import org.spongycastle.util.BigIntegers;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
|
@ -104,8 +106,9 @@ public class Block {
|
|||
}
|
||||
|
||||
public Block getParent() {
|
||||
// TODO retrieve Parent from chain
|
||||
return null;
|
||||
byte[] rlpEncoded = WorldManager.instance.chainDB.get(ByteUtil
|
||||
.longToBytes(this.getNumber() - 1));
|
||||
return new Block(rlpEncoded);
|
||||
}
|
||||
|
||||
public byte[] getParentHash() {
|
||||
|
@ -128,6 +131,11 @@ public class Block {
|
|||
return this.header.getStateRoot();
|
||||
}
|
||||
|
||||
public void setStateRoot(byte[] stateRoot) {
|
||||
if (!parsed) parseRLP();
|
||||
this.header.setStateRoot(stateRoot);
|
||||
}
|
||||
|
||||
public byte[] getTxTrieRoot() {
|
||||
if (!parsed) parseRLP();
|
||||
return this.header.getTxTrieRoot();
|
||||
|
@ -154,7 +162,7 @@ public class Block {
|
|||
}
|
||||
|
||||
public boolean isGenesis() {
|
||||
return this.getNumber() == 0;
|
||||
return this.getNumber() == Genesis.NUMBER;
|
||||
}
|
||||
|
||||
public long getGasLimit() {
|
||||
|
@ -177,6 +185,11 @@ public class Block {
|
|||
return this.header.getNonce();
|
||||
}
|
||||
|
||||
public void setNonce(byte[] nonce) {
|
||||
this.header.setNonce(nonce);
|
||||
rlpEncoded = null;
|
||||
}
|
||||
|
||||
public Trie getTxsState() {
|
||||
return this.txsState;
|
||||
}
|
||||
|
@ -304,13 +317,20 @@ public class Block {
|
|||
public boolean isValid() {
|
||||
boolean isValid = true;
|
||||
|
||||
if(!this.isGenesis()) {
|
||||
// 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
|
||||
//isValid = this.getGasLimit() == this.calcGasLimit();
|
||||
isValid = this.getGasLimit() == this.calcGasLimit();
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -324,7 +344,7 @@ public class Block {
|
|||
return Genesis.GAS_LIMIT;
|
||||
else {
|
||||
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() {
|
||||
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[] transactions = RLP.encodeList();
|
||||
byte[] uncles = RLP.encodeList();
|
||||
|
@ -363,4 +392,13 @@ public class Block {
|
|||
}
|
||||
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() {
|
||||
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);
|
||||
|
@ -186,11 +194,16 @@ public class BlockHeader {
|
|||
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[] nonce = RLP.encodeElement(this.nonce);
|
||||
|
||||
return RLP.encodeList(parentHash, unclesHash, coinbase,
|
||||
stateRoot, txTrieRoot, difficulty, number,
|
||||
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();
|
||||
|
@ -201,7 +214,7 @@ public class BlockHeader {
|
|||
toStringBuff.append(" parentHash=" + ByteUtil.toHexString(parentHash)).append("\n");
|
||||
toStringBuff.append(" unclesHash=" + ByteUtil.toHexString(unclesHash)).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(" difficulty=" + ByteUtil.toHexString(difficulty)).append("\n");
|
||||
toStringBuff.append(" number=" + number).append("\n");
|
||||
|
@ -218,7 +231,7 @@ public class BlockHeader {
|
|||
toStringBuff.append(" parentHash=" + ByteUtil.toHexString(parentHash)).append("");
|
||||
toStringBuff.append(" unclesHash=" + ByteUtil.toHexString(unclesHash)).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(" difficulty=" + ByteUtil.toHexString(difficulty)).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.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
@ -37,6 +38,10 @@ public class ByteUtil {
|
|||
return bytes;
|
||||
}
|
||||
|
||||
public static byte[] longToBytes(long l) {
|
||||
return ByteBuffer.allocate(8).putLong(l).array();
|
||||
}
|
||||
|
||||
public static String toHexString(byte[] data){
|
||||
if (data == null) return "null";
|
||||
else return Hex.toHexString(data);
|
||||
|
@ -155,6 +160,16 @@ public class ByteUtil {
|
|||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static byte[] stripLeadingZeroes(byte[] data){
|
||||
|
||||
if (data == null) return null;
|
||||
|
@ -175,4 +190,19 @@ public class ByteUtil {
|
|||
|
||||
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 org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.net.message.StaticMessages;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.ethereum.core.Block;
|
||||
import org.ethereum.core.Genesis;
|
||||
|
@ -163,28 +165,40 @@ public class BlockTest {
|
|||
|
||||
@Test
|
||||
public void testCalcDifficulty() {
|
||||
byte[] diffBytes = Genesis.getInstance().calcDifficulty();
|
||||
BigInteger difficulty = new BigInteger(1, diffBytes);
|
||||
Block genesis = Genesis.getInstance();
|
||||
BigInteger difficulty = new BigInteger(1, genesis.calcDifficulty());
|
||||
System.out.println("Genesis difficulty = " + difficulty.toString());
|
||||
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));
|
||||
diffBytes = block1.calcDifficulty();
|
||||
difficulty = new BigInteger(1, diffBytes);
|
||||
System.out.println("Block#1 difficulty = " + difficulty.toString());
|
||||
assertEquals(new BigInteger(""), difficulty);
|
||||
BigInteger calcDifficulty = new BigInteger(1, block1.calcDifficulty());
|
||||
BigInteger actualDifficulty = new BigInteger(1, block1.getDifficulty());
|
||||
System.out.println("Block#1 actual difficulty = " + actualDifficulty.toString());
|
||||
System.out.println("Block#1 calculated difficulty = " + calcDifficulty.toString());
|
||||
assertEquals(actualDifficulty, calcDifficulty);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalcGasLimit() {
|
||||
long gasLimit = Genesis.getInstance().calcGasLimit();
|
||||
Block genesis = Genesis.getInstance();
|
||||
long gasLimit = genesis.calcGasLimit();
|
||||
System.out.println("Genesis gasLimit = " + 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
|
||||
Block block1 = new Block(Hex.decode(block_1));
|
||||
gasLimit = block1.calcGasLimit();
|
||||
System.out.println("Block#1 gasLimit = " + gasLimit);
|
||||
assertEquals(999023, gasLimit);
|
||||
long calcGasLimit = block1.calcGasLimit();
|
||||
long actualGasLimit = block1.getGasLimit();
|
||||
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