From 31fddb591be5b00bb165a224c4dd5794dc58d402 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 18:31:22 +0200 Subject: [PATCH 01/11] Avoid double Map search --- .../java/org/ethereum/crypto/HashUtil.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/crypto/HashUtil.java b/ethereumj-core/src/main/java/org/ethereum/crypto/HashUtil.java index 39aecb36..ab14d2ca 100644 --- a/ethereumj-core/src/main/java/org/ethereum/crypto/HashUtil.java +++ b/ethereumj-core/src/main/java/org/ethereum/crypto/HashUtil.java @@ -7,18 +7,16 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.ethereum.db.ByteArrayWrapper; -import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import org.ethereum.util.Utils; import org.spongycastle.util.encoders.Hex; - import org.ethereum.util.LRUMap; public class HashUtil { - private static final int MAX_ENTRIES = 1000; // Should contain most commonly hashed values - private static LRUMap sha3Cache = new LRUMap<>(0, MAX_ENTRIES); - public static final byte[] EMPTY_DATA_HASH = HashUtil.sha3(new byte[0]); + private static final int MAX_ENTRIES = 100; // Should contain most commonly hashed values + private static LRUMap sha3Cache = new LRUMap<>(0, MAX_ENTRIES); + public static final byte[] EMPTY_DATA_HASH = Hex.decode("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); private static final MessageDigest sha256digest; @@ -35,14 +33,15 @@ public class HashUtil { } public static byte[] sha3(byte[] input) { - ByteArrayWrapper inputByteArray = new ByteArrayWrapper(input); - if(sha3Cache.keySet().contains(inputByteArray)) - return sha3Cache.get(inputByteArray); - byte[] result = SHA3Helper.sha3(input); - sha3Cache.put(inputByteArray, result); - return result; + ByteArrayWrapper inputByteArray = new ByteArrayWrapper(input); + byte[] result = sha3Cache.get(inputByteArray); + if(result != null) + return result; + result = SHA3Helper.sha3(input); + sha3Cache.put(inputByteArray, result); + return result; } - + /** * Calculates RIGTMOST160(SHA3(input)). This is used in address calculations. */ @@ -62,7 +61,7 @@ public class HashUtil { byte[] encSender = RLP.encodeElement(addr); byte[] encNonce = RLP.encodeElement(nonce); - byte[] newAddress = HashUtil.sha3omit12(RLP.encodeList(encSender, encNonce)); + byte[] newAddress = sha3omit12(RLP.encodeList(encSender, encNonce)); return newAddress; } From 59d8dfbed029c6a669f9c7f1e6b42492606f3aea Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 18:35:20 +0200 Subject: [PATCH 02/11] Recalculate trie only when necessary --- .../java/org/ethereum/db/ContractDetails.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/db/ContractDetails.java b/ethereumj-core/src/main/java/org/ethereum/db/ContractDetails.java index f2520039..31334881 100644 --- a/ethereumj-core/src/main/java/org/ethereum/db/ContractDetails.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/ContractDetails.java @@ -52,7 +52,7 @@ public class ContractDetails { } } else{ - storageTrie.update(key.getData(), RLP.encodeElement( value.getNoLeadZeroesData() )); + storageTrie.update(key.getData(), RLP.encodeElement(value.getNoLeadZeroesData())); int index = storageKeys.indexOf(key); if (index != -1) { storageKeys.remove(index); @@ -94,7 +94,11 @@ public class ContractDetails { public byte[] getStorageHash() { - getEncoded(); + storageTrie = new Trie(null); + // calc the trie for root hash + for (int i = 0; i < storageKeys.size(); ++i){ + storageTrie.update(storageKeys.get(i).getData(), RLP.encodeElement( storageValues.get(i).getNoLeadZeroesData() )); + } return storageTrie.getRootHash(); } @@ -124,7 +128,7 @@ public class ContractDetails { for (int i = 0; i < keys.size(); ++i) { DataWord key = storageKeys.get(i); DataWord value = storageValues.get(i); - storageTrie.update(key.getData(), RLP.encodeElement( value.getNoLeadZeroesData() )); + storageTrie.update(key.getData(), RLP.encodeElement(value.getNoLeadZeroesData())); } this.code = code.getRLPData(); @@ -149,12 +153,6 @@ public class ContractDetails { values[i] = RLP.encodeElement(value.getNoLeadZeroesData()); } - storageTrie = new Trie(null); - // calc the trie for root hash - for (int i = 0; i < storageKeys.size(); ++i){ - storageTrie.update(storageKeys.get(i).getData(), values[i]); - } - byte[] rlpKeysList = RLP.encodeList(keys); byte[] rlpValuesList = RLP.encodeList(values); byte[] rlpCode = RLP.encodeElement(code); From a5802b939807be0f736d463b2c5e6ba6f29aae9f Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 20:51:08 +0200 Subject: [PATCH 03/11] Improve binToNibble performance --- .../org/ethereum/util/CompactEncoder.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/util/CompactEncoder.java b/ethereumj-core/src/main/java/org/ethereum/util/CompactEncoder.java index 2ab4332d..536144f0 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/CompactEncoder.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/CompactEncoder.java @@ -1,13 +1,14 @@ package org.ethereum.util; -import java.io.ByteArrayOutputStream; - -import static java.util.Arrays.copyOfRange; import static java.util.Arrays.copyOf; +import static java.util.Arrays.copyOfRange; import static org.ethereum.util.ByteUtil.appendByte; import static org.spongycastle.util.Arrays.concatenate; -import static org.spongycastle.util.encoders.Hex.toHexString; +import static org.spongycastle.util.encoders.Hex.encode; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; /** * Compact encoding of hex sequence with optional terminator @@ -85,11 +86,11 @@ public class CompactEncoder { */ public static byte[] unpackToNibbles(byte[] str) { byte[] base = binToNibbles(str); - base = copyOf(base, base.length-1); + base = copyOf(base, base.length - 1); if (base[0] >= 2) { base = appendByte(base, TERMINATOR); } - if (base[0]%2 == 1) { + if (base[0] % 2 == 1) { base = copyOfRange(base, 1, base.length); } else { base = copyOfRange(base, 2, base.length); @@ -101,15 +102,14 @@ public class CompactEncoder { * Transforms a binary array to hexadecimal format + terminator * * @return array with each individual nibble adding a terminator at the end - */ + */ public static byte[] binToNibbles(byte[] str) { - byte[] hexSlice = new byte[0]; - String hexEncoded = toHexString(str); - for (char value : hexEncoded.toCharArray()) { - hexSlice = appendByte(hexSlice, (byte) hexBase.indexOf(value)); + byte[] hexEncoded = encode(str); + ByteBuffer slice = ByteBuffer.allocate(hexEncoded.length + 1); + for (byte b : hexEncoded) { + slice.put((byte)hexBase.indexOf(b)); } - hexSlice = appendByte(hexSlice, TERMINATOR); - - return hexSlice; + slice.put(TERMINATOR); + return slice.array(); } } From 5db586b9115604ccf966595145054e9c6e78d101 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 21:32:33 +0200 Subject: [PATCH 04/11] Improve performance matchingNibbleLength --- ethereumj-core/src/main/java/org/ethereum/trie/Trie.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java index b533fbba..7510c921 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java @@ -400,14 +400,18 @@ public class Trie implements TrieFacade { *******************************/ // Returns the amount of nibbles that match each other from 0 ... - private int matchingNibbleLength(byte[] a, byte[] b) { + public static int matchingNibbleLength(byte[] a, byte[] b) { int i = 0; - while (Arrays.equals(copyOfRange(a, 0, i+1), copyOfRange(b, 0, i+1)) && i < b.length) { + int length = a.length < b.length ? a.length : b.length; + while (i < length) { + if (a[i] != b[i]) + break; i++; } return i; } + // Created an array of empty elements of requred length private Object[] emptyStringSlice(int l) { Object[] slice = new Object[l]; @@ -418,7 +422,6 @@ public class Trie implements TrieFacade { } public byte[] getRootHash() { - Object root = this.getRoot(); if (root == null || (root instanceof byte[] && ((byte[]) root).length == 0) || (root instanceof String && "".equals((String) root))) { From 2e7c40d6dc2ef3b32e0cbc2c74e86ea94f6b4c7a Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 21:50:09 +0200 Subject: [PATCH 05/11] Remove call to WorldManager in Block constructor --- ethereumj-core/src/main/java/org/ethereum/core/Block.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Block.java b/ethereumj-core/src/main/java/org/ethereum/core/Block.java index 745f13c6..5a07e1d1 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -73,9 +73,6 @@ public class Block { timestamp, extraData, nonce); this.txsState = new Trie(null); - byte[] stateRoot = WorldManager.getInstance().getRepository().getRootHash(); - this.header.setStateRoot(stateRoot); - this.header.setTxTrieRoot(txsState.getRootHash()); this.transactionsList = transactionsList; this.uncleList = uncleList; From b7197abe0ed37e27184e1aa5c43817933a4935a8 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 21:53:58 +0200 Subject: [PATCH 06/11] More concice logging program input --- .../org/ethereum/vm/ProgramInvokeFactory.java | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeFactory.java b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeFactory.java index e4f2c13f..66e32b06 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeFactory.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeFactory.java @@ -75,7 +75,7 @@ public class ProgramInvokeFactory { long gaslimit = block.getGasLimit(); if (logger.isInfoEnabled()) { - logger.info("Program invocation: \n" + + logger.info("Top level call: \n" + "address={}\n" + "origin={}\n" + "caller={}\n" + @@ -144,7 +144,7 @@ public class ProgramInvokeFactory { if (logger.isInfoEnabled()) { - logger.info("Program invocation: \n" + + logger.info("Internal call: \n" + "address={}\n" + "origin={}\n" + "caller={}\n" + @@ -152,27 +152,15 @@ public class ProgramInvokeFactory { "gasPrice={}\n" + "gas={}\n" + "callValue={}\n" + - "data={}\n" + - "lastHash={}\n" + - "coinbase={}\n" + - "timestamp={}\n" + - "blockNumber={}\n" + - "difficulty={}\n" + - "gaslimit={}\n", - Hex.toHexString(address.getData()), - Hex.toHexString(origin.getData()), - Hex.toHexString(caller.getData()), - new BigInteger(balance.getData()).longValue(), - new BigInteger(gasPrice.getData()).longValue(), - new BigInteger(gas.getData()).longValue(), - Hex.toHexString(callValue.getData()), - data == null ? "null": Hex.toHexString(data), - Hex.toHexString(lastHash.getData()), - Hex.toHexString(coinbase.getData()), - Hex.toHexString(timestamp.getData()), - new BigInteger(number.getData()).longValue(), - Hex.toHexString(difficulty.getData()), - new BigInteger(gasLimit.getData()).longValue()); + "data={}\n", + Hex.toHexString(address.getLast20Bytes()), + Hex.toHexString(origin.getLast20Bytes()), + Hex.toHexString(caller.getLast20Bytes()), + balance.longValue(), + gasPrice.longValue(), + gas.longValue(), + callValue.longValue(), + data == null ? "null": Hex.toHexString(data)); } return new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue, From 6f84df14776df43a20d8ccb2a1bd8934d7a7a32e Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 22:01:23 +0200 Subject: [PATCH 07/11] Remove quotes around string in property file --- ethereumj-core/src/main/resources/system.properties | 4 ++-- ethereumj-core/src/test/resources/system.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ethereumj-core/src/main/resources/system.properties b/ethereumj-core/src/main/resources/system.properties index 0b3d94c6..89a74fcd 100644 --- a/ethereumj-core/src/main/resources/system.properties +++ b/ethereumj-core/src/main/resources/system.properties @@ -84,7 +84,7 @@ samples.dir = samples # the existing database will be # destroyed and all the data will be # downloaded from peers again -database.reset = false +database.reset = true # place to save physical storage files database.dir = database @@ -92,7 +92,7 @@ database.dir = database # this string is computed # to be eventually the address # that get the miner reward -coinbase.secret = "monkey" +coinbase.secret = monkey # for testing purposes # all the state will be dumped diff --git a/ethereumj-core/src/test/resources/system.properties b/ethereumj-core/src/test/resources/system.properties index 64f774cd..5bf7858f 100644 --- a/ethereumj-core/src/test/resources/system.properties +++ b/ethereumj-core/src/test/resources/system.properties @@ -73,4 +73,4 @@ database.reset = true # this string is computed # to be eventually the address # that get the miner reward -coinbase.secret = "monkey" \ No newline at end of file +coinbase.secret = monkey \ No newline at end of file From adc23133088f60bc355a3700cd29806fe2b957ed Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 22:05:40 +0200 Subject: [PATCH 08/11] Change VM log to be more like pyethereum --- .../src/main/java/org/ethereum/vm/VM.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java index 4d30a7d7..56cdd38e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -65,10 +65,14 @@ public class VM { int oldMemSize = program.getMemSize(); + String hint = ""; + long gasBefore = program.getGas().longValue(); + int stepBefore = program.getPC(); + switch (OpCode.code(op)) { case SHA3: - program.spendGas(GasCost.SHA3, OpCode.code(op).name()); - break; + program.spendGas(GasCost.SHA3, OpCode.code(op).name()); + break; case SLOAD: program.spendGas(GasCost.SLOAD, OpCode.code(op).name()); break; @@ -89,8 +93,7 @@ public class VM { program.spendGas(GasCost.STEP, OpCode.code(op).name()); break; } - - String hint = ""; + switch (OpCode.code(op)) { /** * Stop and Arithmetic Operations @@ -271,7 +274,7 @@ public class VM { DataWord word2 = program.stackPop(); if (logger.isInfoEnabled()) - hint = word1.longValue() + " = " + word2.longValue(); + hint = word1.longValue() + " == " + word2.longValue(); if (word1.xor(word2).isZero()) { word1.and(DataWord.ZERO); @@ -696,9 +699,10 @@ public class VM { DataWord inSize = program.stackPop(); if (logger.isInfoEnabled()) - logger.info("[ {} ] Op: [ {} ] Gas: [ {} ] Deep: [ {} ] Hint: [ {} ]" ,program.getPC(), - OpCode.code(op).name(), program.getGas().longValue(), - program.invokeData.getCallDeep(), hint); + logger.info("[ {} ] Op: [ {} ] Gas: [ {} ] Deep: [ {} ] Hint: [ {} ]", + program.getPC(), OpCode.code(op).name(), program + .getGas().longValue(), program.invokeData + .getCallDeep(), hint); program.createContract(value, inOffset, inSize); program.step(); @@ -719,7 +723,7 @@ public class VM { OpCode.code(op).name(), program.getGas().longValue(), program.invokeData.getCallDeep(), hint); - program.callToAddress(gas, toAddress, value, inDataOffs, inDataSize,outDataOffs, outDataSize); + program.callToAddress(gas, toAddress, value, inDataOffs, inDataSize, outDataOffs, outDataSize); program.step(); } break; @@ -744,14 +748,13 @@ public class VM { default:{ } } - - if (logger.isInfoEnabled()) - if (!OpCode.code(op).equals(CALL) && !OpCode.code(op).equals(CREATE)) - logger.info("[ {} ] Op: [ {} ] Gas: [ {} ] Deep: [ {} ] Hint: [ {} ]" ,program.getPC(), - OpCode.code(op).name(), program.getGas().longValue(), - program.invokeData.getCallDeep(), hint); - - + + if (logger.isInfoEnabled()) + if (!OpCode.code(op).equals(CALL) && !OpCode.code(op).equals(CREATE)) + logger.info("[ {} ] Op: [ {} ] Gas: [ {} ] Deep: [ {} ] Hint: [ {} ]", + stepBefore, OpCode.code(op).name(), gasBefore, program.invokeData + .getCallDeep(), hint); + // memory gas calc int newMemSize = program.getMemSize(); int memoryUsage = (newMemSize - oldMemSize) /32; From ac8d2a96f961ed66cce1051e65e1445d465dd5fe Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 22:43:18 +0200 Subject: [PATCH 09/11] Outline VM log --- .../main/java/org/ethereum/vm/Program.java | 12 +++++------ .../src/main/java/org/ethereum/vm/VM.java | 20 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java index cc6f5180..e6e901a9 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -306,7 +306,7 @@ public class Program { this.refundGas(refundGas, "remain gas from the internal call"); if (logger.isInfoEnabled()){ - logger.info("The remain gas refunded, account: [ {} ], gas: [ {} ] ", + logger.info("The remaining gas is refunded, account: [ {} ], gas: [ {} ] ", Hex.toHexString(this.getOwnerAddress().getLast20Bytes()), refundGas); } @@ -317,7 +317,7 @@ public class Program { * That method implement internal calls * and code invocations * - * @param gas - gas to pay for the call, remain gas will be refunded to the caller + * @param gas - gas to pay for the call, remaining gas will be refunded to the caller * @param toAddressDW - address to call * @param endowmentValue - the value that can be transfer along with the code execution * @param inDataOffs - start of memory to be input data to the call @@ -431,13 +431,13 @@ public class Program { BigInteger refundGas = gas.value().subtract(BigInteger.valueOf(result.getGasUsed())); if (refundGas.compareTo(BigInteger.ZERO) == 1) { - this.refundGas(refundGas.intValue(), "remain gas from the internal call"); - logger.info("The remain gas refunded, account: [ {} ], gas: [ {} ] ", - refundGas.toString(), refundGas.toString()); + this.refundGas(refundGas.intValue(), "remaining gas from the internal call"); + logger.info("The remaining gas refunded, account: [ {} ], gas: [ {} ] ", + Hex.toHexString(senderAddress), refundGas.toString()); } } else { - this.refundGas(gas.intValue(), "remain gas from the internal call"); + this.refundGas(gas.intValue(), "remaining gas from the internal call"); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java index 56cdd38e..db7e3d1a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -55,6 +55,7 @@ public class VM { private Logger logger = LoggerFactory.getLogger("VM"); private static BigInteger _32_ = BigInteger.valueOf(32); + private static String logString = "[ {} ]\t Op: [ {} ]\t Gas: [ {} ]\t Deep: [ {} ] Hint: [ {} ]"; public void step(Program program) { @@ -699,10 +700,10 @@ public class VM { DataWord inSize = program.stackPop(); if (logger.isInfoEnabled()) - logger.info("[ {} ] Op: [ {} ] Gas: [ {} ] Deep: [ {} ] Hint: [ {} ]", - program.getPC(), OpCode.code(op).name(), program - .getGas().longValue(), program.invokeData - .getCallDeep(), hint); + logger.info(logString, program.getPC(), OpCode.code(op) + .name(), program.getGas().longValue(), + program.invokeData.getCallDeep(), hint); + program.createContract(value, inOffset, inSize); program.step(); @@ -719,9 +720,9 @@ public class VM { DataWord outDataSize = program.stackPop(); if (logger.isInfoEnabled()) - logger.info("[ {} ] Op: [ {} ] Gas: [ {} ] Deep: [ {} ] Hint: [ {} ]" ,program.getPC(), - OpCode.code(op).name(), program.getGas().longValue(), - program.invokeData.getCallDeep(), hint); + logger.info(logString, program.getPC(), OpCode.code(op) + .name(), program.getGas().longValue(), + program.invokeData.getCallDeep(), hint); program.callToAddress(gas, toAddress, value, inDataOffs, inDataSize, outDataOffs, outDataSize); @@ -751,9 +752,8 @@ public class VM { if (logger.isInfoEnabled()) if (!OpCode.code(op).equals(CALL) && !OpCode.code(op).equals(CREATE)) - logger.info("[ {} ] Op: [ {} ] Gas: [ {} ] Deep: [ {} ] Hint: [ {} ]", - stepBefore, OpCode.code(op).name(), gasBefore, program.invokeData - .getCallDeep(), hint); + logger.info(logString, stepBefore, OpCode.code(op).name(), + gasBefore, program.invokeData.getCallDeep(), hint); // memory gas calc int newMemSize = program.getMemSize(); From 028dc5f1d4992d47427ad460b0d0fec72c3783bc Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 3 Aug 2014 23:02:54 +0200 Subject: [PATCH 10/11] Small refactoring --- .../main/java/org/ethereum/core/Genesis.java | 14 ++++++----- .../main/java/org/ethereum/db/Repository.java | 25 +++++++------------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java index 2a4d1ccb..2f53d28a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java @@ -1,6 +1,7 @@ package org.ethereum.core; import org.ethereum.crypto.HashUtil; +import org.ethereum.db.Repository; import org.ethereum.manager.WorldManager; import org.ethereum.util.RLP; import org.slf4j.Logger; @@ -64,17 +65,18 @@ public class Genesis extends Block { NUMBER, MIN_GAS_PRICE, GAS_LIMIT, GAS_USED, TIMESTAMP, EXTRA_DATA, NONCE, null, null); + Repository repository = WorldManager.getInstance().getRepository(); // The proof-of-concept series include a development premine, making the state root hash // some value stateRoot. The latest documentation should be consulted for the value of the state root. for (String address : premine) { - WorldManager.getInstance().getRepository().createAccount(Hex.decode(address)); - WorldManager.getInstance().getRepository().addBalance (Hex.decode(address), BigInteger.valueOf(2).pow(200) ); + repository.createAccount(Hex.decode(address)); + repository.addBalance (Hex.decode(address), BigInteger.valueOf(2).pow(200) ); } - this.setStateRoot(WorldManager.getInstance().getRepository().getRootHash()); - WorldManager.getInstance().getRepository().dumpState(0, 0, null); + setStateRoot(repository.getWorldState().getRootHash()); + repository.dumpState(0, 0, null); - logger.info("Genesis-hash: " + Hex.toHexString(this.getHash())); - logger.info("Genesis-stateRoot: " + Hex.toHexString(this.getStateRoot())); + logger.info("Genesis-hash: {}", Hex.toHexString(this.getHash())); + logger.info("Genesis-stateRoot: {}", Hex.toHexString(this.getStateRoot())); } public static Block getInstance() { diff --git a/ethereumj-core/src/main/java/org/ethereum/db/Repository.java b/ethereumj-core/src/main/java/org/ethereum/db/Repository.java index 1de3bc7b..13242538 100644 --- a/ethereumj-core/src/main/java/org/ethereum/db/Repository.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/Repository.java @@ -7,7 +7,6 @@ import org.ethereum.core.Blockchain; import org.ethereum.core.Genesis; import org.ethereum.crypto.HashUtil; import org.ethereum.json.JSONHelper; -import org.ethereum.manager.WorldManager; import org.ethereum.trie.TrackTrie; import org.ethereum.trie.Trie; import org.ethereum.util.ByteUtil; @@ -181,9 +180,9 @@ public class Repository { byte[] accountStateRLP = accountStateDB.get(addr); - if (accountStateRLP.length == 0) { + if (accountStateRLP.length == 0) return null; - } + AccountState state = new AccountState(accountStateRLP); return state; } @@ -196,11 +195,10 @@ public class Repository { logger.debug("Get contract details for: [ {} ]", Hex.toHexString(addr)); byte[] accountDetailsRLP = contractDetailsDB.get(addr); - - if (accountDetailsRLP == null) { - return null; - } - + + if (accountDetailsRLP == null) + return null; + if (logger.isDebugEnabled()) logger.debug("Contract details RLP: [ {} ]", Hex.toHexString(accountDetailsRLP)); @@ -214,10 +212,9 @@ public class Repository { AccountState state = getAccountState(addr); - if (state == null){ + if (state == null) state = createAccount(addr); - } - + BigInteger newBalance = state.addToBalance(value); if (logger.isDebugEnabled()) @@ -347,10 +344,6 @@ public class Repository { contractDetailsDB.delete(addr); } - public byte[] getRootHash() { - return this.worldState.getRootHash(); - } - public List dumpKeys(){ return stateDB.dumpKeys(); } @@ -413,7 +406,7 @@ public class Repository { bw.write("\n"); } - String rootHash = Hex.toHexString(WorldManager.getInstance().getRepository().getRootHash()); + String rootHash = Hex.toHexString(this.getWorldState().getRootHash()); bw.write( String.format(" => Global State Root: [ %s ]", rootHash) ); From e25fbc90cbc47eb7d5a94b16d353ba494d6caea4 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Mon, 4 Aug 2014 00:19:25 +0200 Subject: [PATCH 11/11] Add Unit tests for matchingNibbleLength and move to ByteUtil --- .../src/main/java/org/ethereum/trie/Trie.java | 14 +---- .../main/java/org/ethereum/util/ByteUtil.java | 18 ++++++ .../java/org/ethereum/util/ByteUtilTest.java | 60 +++++++++++++++++++ 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java index 7510c921..7a806d83 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java @@ -3,6 +3,7 @@ package org.ethereum.trie; import static java.util.Arrays.copyOfRange; import static org.spongycastle.util.Arrays.concatenate; import static org.ethereum.util.CompactEncoder.*; +import static org.ethereum.util.ByteUtil.matchingNibbleLength; import java.util.Arrays; @@ -399,19 +400,6 @@ public class Trie implements TrieFacade { * Utility functions * *******************************/ - // Returns the amount of nibbles that match each other from 0 ... - public static int matchingNibbleLength(byte[] a, byte[] b) { - int i = 0; - int length = a.length < b.length ? a.length : b.length; - while (i < length) { - if (a[i] != b[i]) - break; - i++; - } - return i; - } - - // Created an array of empty elements of requred length private Object[] emptyStringSlice(int l) { Object[] slice = new Object[l]; diff --git a/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java b/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java index 503a7e89..d79a530d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java @@ -60,6 +60,24 @@ public class ByteUtil { return data; } + /** + * Returns the amount of nibbles that match each other from 0 ... + * amount will never be larger than smallest input + * + * @param a - first input + * @param b second input + * @return number of bytes that match + */ + public static int matchingNibbleLength(byte[] a, byte[] b) { + int i = 0; + int length = a.length < b.length ? a.length : b.length; + while (i < length) { + if (a[i] != b[i]) + break; + i++; + } + return i; + } public static byte[] longToBytes(long l) { return ByteBuffer.allocate(8).putLong(l).array(); diff --git a/ethereumj-core/src/test/java/org/ethereum/util/ByteUtilTest.java b/ethereumj-core/src/test/java/org/ethereum/util/ByteUtilTest.java index e2c77a30..30ecb5db 100644 --- a/ethereumj-core/src/test/java/org/ethereum/util/ByteUtilTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/util/ByteUtilTest.java @@ -62,6 +62,66 @@ public class ByteUtilTest { assertArrayEquals(expected, ByteUtil.stripLeadingZeroes(test1)); assertArrayEquals(expected, ByteUtil.stripLeadingZeroes(test2)); } + + @Test + public void testMatchingNibbleLength1() { + // a larger than b + byte[] a = new byte[] { 0x00, 0x01 }; + byte[] b = new byte[] { 0x00 }; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(1, result); + } + @Test + public void testMatchingNibbleLength2() { + // b larger than a + byte[] a = new byte[] { 0x00 }; + byte[] b = new byte[] { 0x00, 0x01 }; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(1, result); + } + + @Test + public void testMatchingNibbleLength3() { + // a and b the same length equal + byte[] a = new byte[] { 0x00 }; + byte[] b = new byte[] { 0x00 }; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(1, result); + } + + @Test + public void testMatchingNibbleLength4() { + // a and b the same length not equal + byte[] a = new byte[] { 0x01 }; + byte[] b = new byte[] { 0x00 }; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(0, result); + } + + @Test(expected=NullPointerException.class) + public void testMatchingNibbleLength5() { + // a == null + byte[] a = null; + byte[] b = new byte[] { 0x00 }; + ByteUtil.matchingNibbleLength(a, b); + } + + @Test(expected=NullPointerException.class) + public void testMatchingNibbleLength6() { + // b == null + byte[] a = new byte[] { 0x00 }; + byte[] b = null; + ByteUtil.matchingNibbleLength(a, b); + } + + @Test + public void testMatchingNibbleLength7() { + // a or b is empty + byte[] a = new byte[0]; + byte[] b = new byte[] { 0x00 }; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(0, result); + } /** * This test shows the difference between iterating over,