From e106d2fdc7761da3d8f581253ed9e4a8c863764b Mon Sep 17 00:00:00 2001 From: nicksavers Date: Mon, 1 Sep 2014 20:59:06 +0200 Subject: [PATCH] Add VM dumptrace in pretty format --- .../org/ethereum/config/SystemProperties.java | 7 + .../main/java/org/ethereum/core/Block.java | 2 +- .../java/org/ethereum/core/Blockchain.java | 13 +- .../src/main/java/org/ethereum/trie/Trie.java | 50 ++--- .../main/java/org/ethereum/util/ByteUtil.java | 18 ++ .../org/ethereum/util/CompactEncoder.java | 56 ++--- .../main/java/org/ethereum/util/Value.java | 61 ++---- .../main/java/org/ethereum/vm/DataWord.java | 5 + .../main/java/org/ethereum/vm/Program.java | 45 +++- .../src/main/java/org/ethereum/vm/VM.java | 180 ++++++++++------ .../main/resources/log4j-detailed.properties | 2 +- .../src/main/resources/log4j.properties | 11 +- .../src/main/resources/system.properties | 14 +- .../test/java/org/ethereum/trie/TrieTest.java | 202 +++++++++--------- .../java/org/ethereum/util/ByteUtilTest.java | 14 ++ .../org/ethereum/util/CompactEncoderTest.java | 18 -- 16 files changed, 388 insertions(+), 310 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java b/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java index 9a4860d0..eb3f2683 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java @@ -29,6 +29,7 @@ public class SystemProperties { private static Boolean DEFAULT_DB_RESET = false; private static Boolean DEFAULT_DUMP_FULL = false; private static String DEFAULT_DUMP_DIR = "dmp"; + private static String DEFAULT_DUMP_STYLE = "standard+"; private static Integer DEFAULT_VMTRACE_BLOCK = 0; private static String DEFAULT_DATABASE_DIR = System.getProperty("user.dir"); private static Boolean DEFAULT_DUMP_CLEAN_ON_RESTART = true; @@ -152,11 +153,17 @@ public class SystemProperties { return prop.getProperty("dump.dir"); } + public String dumpStyle() { + if(prop.isEmpty()) return DEFAULT_DUMP_STYLE; + return prop.getProperty("dump.style"); + } + public Integer dumpBlock() { if(prop.isEmpty()) return DEFAULT_VMTRACE_BLOCK; return Integer.parseInt(prop.getProperty("dump.block")); } + public String databaseDir() { if(prop.isEmpty()) return DEFAULT_DATABASE_DIR; return prop.getProperty("database.dir"); 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 b65afe63..08d4a5e5 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -32,7 +32,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public class Block { - private static Logger logger = LoggerFactory.getLogger("block"); + private static final Logger logger = LoggerFactory.getLogger("block"); /* A scalar value equal to the mininum limit of gas expenditure per block */ private static long MIN_GAS_LIMIT = 125000L; diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java index be896cc3..83d2f3ee 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java @@ -96,8 +96,8 @@ public class Blockchain { if (block == null) return; - if (block.getNumber() == 12301) - logger.debug("Block #12301"); + if (block.getNumber() == 12390) + logger.debug("Block #12390"); // if it is the first block to add // make sure the parent is genesis @@ -153,11 +153,14 @@ public class Blockchain { for (TransactionReceipt txr : block.getTxReceiptList()) { stateLogger.debug("apply block: [ {} ] tx: [ {} ] ", block.getNumber(), i); totalGasUsed += applyTransaction(block, txr.getTransaction()); - if(!Arrays.equals(this.repository.getWorldState().getRootHash(), txr.getPostTxState())) - stateLogger.warn("TX: STATE CONFLICT {}..: {}", Hex.toHexString(txr.getTransaction().getHash()).substring(0, 8), - Hex.toHexString(this.repository.getWorldState().getRootHash())); if(block.getNumber() >= CONFIG.traceStartBlock()) repository.dumpState(block, totalGasUsed, i++, txr.getTransaction().getHash()); + if(!Arrays.equals(this.repository.getWorldState().getRootHash(), txr.getPostTxState())) { + stateLogger.warn("TX: STATE CONFLICT {}..: {}", Hex.toHexString(txr.getTransaction().getHash()).substring(0, 8), + Hex.toHexString(this.repository.getWorldState().getRootHash())); +// repository.close(); +// System.exit(-1); // Don't continue + } } this.addReward(block); 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 12d17296..226fc200 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java @@ -1,12 +1,13 @@ 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 static org.ethereum.util.CompactEncoder.binToNibbles; +import static org.ethereum.util.CompactEncoder.packNibbles; +import static org.ethereum.util.CompactEncoder.unpackToNibbles; +import static org.spongycastle.util.Arrays.concatenate; import java.util.*; -import java.util.concurrent.ConcurrentLinkedQueue; import org.ethereum.crypto.HashUtil; import org.ethereum.db.ByteArrayWrapper; @@ -253,11 +254,9 @@ public class Trie implements TrieFacade { } if (matchingLength == 0) { - // End of the chain, return return newHash; } else { - Object[] newNode = new Object[] { packNibbles(copyOfRange(key, 0, matchingLength)), newHash}; return this.putToCache(newNode); } @@ -300,7 +299,6 @@ public class Trie implements TrieFacade { } else { newNode = new Object[] {currentNode.get(0).asString(), hash}; } - return this.putToCache(newNode); } else { return node; @@ -337,7 +335,6 @@ public class Trie implements TrieFacade { } else { newNode = itemList; } - return this.putToCache(newNode); } } @@ -449,7 +446,7 @@ public class Trie implements TrieFacade { * safe, the tree should not be modified during the * cleaning process. */ - public void cleanCacheGarbage(){ + public void cleanCacheGarbage() { CollectFullSetOfNodes collectAction = new CollectFullSetOfNodes(); long startTime = System.currentTimeMillis(); @@ -460,15 +457,13 @@ public class Trie implements TrieFacade { Map nodes = this.getCache().getNodes(); Set toRemoveSet = new HashSet<>(); - for (ByteArrayWrapper key : nodes.keySet()){ - - if (!hashSet.contains(key.getData())){ - + for (ByteArrayWrapper key : nodes.keySet()) { + if (!hashSet.contains(key.getData())) { toRemoveSet.add(key); } } - for (ByteArrayWrapper key : toRemoveSet){ + for (ByteArrayWrapper key : toRemoveSet) { this.getCache().delete(key.getData()); @@ -476,29 +471,23 @@ public class Trie implements TrieFacade { logger.trace("Garbage collected node: [ {} ]", Hex.toHexString( key.getData() )); } - logger.info("Garbage collected node list, size: [ {} ]", toRemoveSet.size()); logger.info("Garbage collection time: [ {}ms ]", System.currentTimeMillis() - startTime); } - - public void scanTree(byte[] hash , ScanAction scanAction){ + public void scanTree(byte[] hash, ScanAction scanAction) { Value node = this.getCache().get(hash); - if (node == null) return ; - - if (node.isList()){ + if (node == null) return; + if (node.isList()) { List siblings = node.asList(); - if (siblings.size() == 2){ + if (siblings.size() == PAIR_SIZE) { Value val = new Value(siblings.get(1)); if (val.isHashCode()) scanTree(val.asBytes(), scanAction); - } else { - - for (int j = 0; j < 17; ++j){ - + for (int j = 0; j < LIST_SIZE; ++j) { Value val = new Value(siblings.get(j)); if (val.isHashCode()) scanTree(val.asBytes(), scanAction); @@ -508,26 +497,21 @@ public class Trie implements TrieFacade { } } - public String getTrieDump(){ + public String getTrieDump() { String root = ""; TraceAllNodes traceAction = new TraceAllNodes(); this.scanTree(this.getRootHash(), traceAction); - if (this.getRoot() instanceof Value){ - + if (this.getRoot() instanceof Value) { root = "root: " + Hex.toHexString(getRootHash()) + " => " + this.getRoot() + "\n"; - } else{ - + } else { root = "root: " + Hex.toHexString(getRootHash()) + "\n"; } - return root + traceAction.getOutput(); } - - public interface ScanAction{ - + public interface ScanAction { public void doOnNode(byte[] hash, Value node); } } 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 d79a530d..8832cd53 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java @@ -116,6 +116,24 @@ public class ByteUtil { return 0; return new BigInteger(1, b).intValue(); } + + /** + * Turn nibbles to a pretty looking output string + * + * Example. [ 1, 2, 3, 4, 5 ] becomes '\x11\x23\x45' + * + * @param nibbles - getting byte of data [ 04 ] and turning + * it to a '\x04' representation + * @return pretty string of nibbles + */ + public static String nibblesToPrettyString(byte[] nibbles){ + StringBuffer buffer = new StringBuffer(); + for (byte nibble : nibbles) { + String nibleString = Utils.oneByteToHexString(nibble); + buffer.append("\\x" + nibleString); + } + return buffer.toString(); + } /** * Calculate the number of bytes need 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 ebc3030d..2d3ca94e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/CompactEncoder.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/CompactEncoder.java @@ -51,25 +51,24 @@ public class CompactEncoder { private final static byte TERMINATOR = 16; private final static Map hexMap = new HashMap<>(); static { - hexMap.put('0', (byte)0); - hexMap.put('1', (byte)1); - hexMap.put('2', (byte)2); - hexMap.put('3', (byte)3); - hexMap.put('4', (byte)4); - hexMap.put('5', (byte)5); - hexMap.put('6', (byte)6); - hexMap.put('7', (byte)7); - hexMap.put('8', (byte)8); - hexMap.put('9', (byte)9); - hexMap.put('a', (byte)10); - hexMap.put('b', (byte)11); - hexMap.put('c', (byte)12); - hexMap.put('d', (byte)13); - hexMap.put('e', (byte)14); - hexMap.put('f', (byte)15); + hexMap.put('0', (byte)0x0); + hexMap.put('1', (byte)0x1); + hexMap.put('2', (byte)0x2); + hexMap.put('3', (byte)0x3); + hexMap.put('4', (byte)0x4); + hexMap.put('5', (byte)0x5); + hexMap.put('6', (byte)0x6); + hexMap.put('7', (byte)0x7); + hexMap.put('8', (byte)0x8); + hexMap.put('9', (byte)0x9); + hexMap.put('a', (byte)0xa); + hexMap.put('b', (byte)0xb); + hexMap.put('c', (byte)0xc); + hexMap.put('d', (byte)0xd); + hexMap.put('e', (byte)0xe); + hexMap.put('f', (byte)0xf); } - /** * Pack nibbles to binary * @@ -94,7 +93,7 @@ public class CompactEncoder { } ByteArrayOutputStream buffer = new ByteArrayOutputStream(); for (int i = 0; i < nibbles.length; i += 2) { - buffer.write( 16*nibbles[i] + nibbles[i+1] ); + buffer.write(16*nibbles[i] + nibbles[i+1]); } return buffer.toByteArray(); } @@ -140,27 +139,6 @@ public class CompactEncoder { for (byte b : hexEncoded) { slice.put(hexMap.get((char)b)); } - return slice.array(); } - - /** - * turn nibbles to a nice good looking output string - * - * @param nibbles - getting byte of data [ 04 ] and turning - * it to a '\x04' representation - * @return - */ - public static String nibblesToPrettyString(byte[] nibbles){ - - StringBuffer buffer = new StringBuffer(); - for (byte nibble : nibbles){ - - String nibleString = Utils.oneByteToHexString(nibble); - - buffer.append("\\x" + nibleString); - } - - return buffer.toString(); - } } diff --git a/ethereumj-core/src/main/java/org/ethereum/util/Value.java b/ethereumj-core/src/main/java/org/ethereum/util/Value.java index 8597c1f6..4d7e2511 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/Value.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/Value.java @@ -1,6 +1,5 @@ package org.ethereum.util; -import java.lang.reflect.Array; import java.math.BigInteger; import java.util.Arrays; import java.util.List; @@ -155,7 +154,6 @@ public class Value { } for (int i = 0; i < data.length; ++i){ - if (data[i] > 32 && data[i] < 126) ++readableChars; } @@ -171,14 +169,14 @@ public class Value { int hexChars = 0; byte[] data = (byte[])value; - for (int i = 0; i < data.length; ++i){ + for (int i = 0; i < data.length; ++i) { - if ((data[i] >= 48 && data[i] <= 57) || - (data[i] >= 97 && data[i] <= 102) - ) ++hexChars; - } + if ((data[i] >= 48 && data[i] <= 57) + || (data[i] >= 97 && data[i] <= 102)) + ++hexChars; + } - if ((double)hexChars / (double)data.length > 0.9) + if ((double) hexChars / (double) data.length > 0.9) return true; else return false; @@ -212,23 +210,23 @@ public class Value { return 0; } - public String toString(){ + public String toString() { StringBuffer buffer = new StringBuffer(); - if (isList()){ + if (isList()) { - Object[] list = (Object[])value; + Object[] list = (Object[]) value; // special case - key/value node - if (list.length == 2){ + if (list.length == 2) { buffer.append("[ "); Value key = new Value(list[0]); byte[] keyNibbles = CompactEncoder.binToNibblesNoTerminator(key.asBytes()); - String keyString = CompactEncoder.nibblesToPrettyString(keyNibbles); + String keyString = ByteUtil.nibblesToPrettyString(keyNibbles); buffer.append(keyString); buffer.append(","); @@ -239,83 +237,60 @@ public class Value { buffer.append(" ]"); return buffer.toString(); } - buffer.append(" ["); for (int i = 0; i < list.length; ++i){ - Value val = new Value(list[i]); - if (val.isString() || val.isEmpty()){ - - buffer.append("'"). - append(val.toString()) - .append("'"); + buffer.append("'").append(val.toString()).append("'"); } else { - buffer.append(val.toString()); } - if (i < list.length - 1) buffer.append(", "); } buffer.append("] "); return buffer.toString(); - } else if (isEmpty()){ + } else if (isEmpty()) { return ""; - } else if (isBytes()){ + } else if (isBytes()) { StringBuffer output = new StringBuffer(); if (isHashCode()) { output.append(Hex.toHexString(asBytes())); - }else if (isReadbleString()){ - + } else if (isReadbleString()) { output.append("'"); - for (byte oneByte : asBytes()){ - if (oneByte < 16){ + for (byte oneByte : asBytes()) { + if (oneByte < 16) { output.append("\\x").append(Utils.oneByteToHexString(oneByte)); } else { - output.append( Character.valueOf((char)oneByte) ); } } - output.append("'"); return output.toString(); } - return Hex.toHexString(this.asBytes()); } else if (isString()){ - return asString(); } - - return "what the fuck"; + return "Unexpected type"; } - - public int countYourBranchNodes(){ if (this.isList()){ List objList = this.asList(); - int i = 0; for (Object obj : objList){ - i += (new Value(obj)).countYourBranchNodes(); } - return i; } else if (this.isBytes()){ - this.asBytes(); - } - - return 0; } } diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java b/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java index fdf0b4c9..ae322de6 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java @@ -265,6 +265,11 @@ public class DataWord implements Comparable { public String toString() { return Hex.toHexString(data); } + + public String shortHex() { + String hexValue = Hex.toHexString(getNoLeadZeroesData()).toUpperCase(); + return "0x" + hexValue.replaceFirst("^0+(?!$)", ""); + } public DataWord clone() { return new DataWord(Arrays.clone(data)); 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 f895a3d2..5ec01ef3 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -11,7 +11,10 @@ import org.spongycastle.util.encoders.Hex; import java.math.BigInteger; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Stack; /** @@ -185,7 +188,7 @@ public class Program { int offset = offsetData.intValue(); int size = sizeData.intValue(); byte[] chunk = new byte[size]; - allocateMemory(offset, chunk); + allocateMemory(offset, new byte[size]); if (memory != null) { if (memory.limit() < offset + size) size = memory.limit() - offset; @@ -424,8 +427,12 @@ public class Program { if (retSize > allocSize) { byte[] outArray = Arrays.copyOf(buffer.array(), allocSize); this.memorySave(outArray, buffer.array()); - } else { + } else if (retSize == 0 || retSize == allocSize){ this.memorySave(outDataOffs.getData(), buffer.array()); + } else { + byte[] outArray = new byte[allocSize]; + System.arraycopy(buffer.array(), 0, outArray, 0, retSize); + this.memorySave(outDataOffs.getData(), outArray); } } } @@ -562,6 +569,34 @@ public class Program { public void setRuntimeFailure(RuntimeException e) { result.setException(e); } + + public String memoryToString() { + StringBuilder memoryData = new StringBuilder(); + StringBuilder firstLine = new StringBuilder(); + StringBuilder secondLine = new StringBuilder(); + for (int i = 0; memory != null && i < memory.limit(); ++i) { + + byte value = memory.get(i); + // Check if value is ASCII + // (should be until 0x7e - but using 0x7f + // to be compatible with cpp-ethereum) + // See: https://github.com/ethereum/cpp-ethereum/issues/299 + String character = ((byte)0x20 <= value && value <= (byte)0x7f) ? new String(new byte[]{value}) : "?"; + firstLine.append(character).append(""); + secondLine.append(Utils.oneByteToHexString(value)).append(" "); + + if ((i + 1) % 8 == 0) { + String tmp = String.format("%4s", Integer.toString(i - 7, 16)).replace(" ", "0"); + memoryData.append("").append(tmp).append(" "); + memoryData.append(firstLine).append(" "); + memoryData.append(secondLine); + if (i+1 < memory.limit()) memoryData.append("\n"); + firstLine.setLength(0); + secondLine.setLength(0); + } + } + return memoryData.toString(); + } public void fullTrace() { @@ -577,7 +612,9 @@ public class Program { ContractDetails contractDetails = this.result.getRepository(). getContractDetails(this.programAddress.getLast20Bytes()); StringBuilder storageData = new StringBuilder(); - for (DataWord key : contractDetails.getStorage().keySet()) { + List storageKeys = new ArrayList<>(contractDetails.getStorage().keySet()); + Collections.sort((List) storageKeys); + for (DataWord key : storageKeys) { storageData.append(" ").append(key).append(" -> "). append(contractDetails.getStorage().get(key)).append("\n"); } @@ -625,8 +662,6 @@ public class Program { invokeData.getGas().longValue(), getGas().longValue()); - - StringBuilder globalOutput = new StringBuilder("\n"); if (stackData.length() > 0) stackData.append("\n"); 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 8f6bb23b..2263846f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -64,12 +64,17 @@ public class VM { private Logger logger = LoggerFactory.getLogger("VM"); private Logger dumpLogger = LoggerFactory.getLogger("dump"); private static BigInteger _32_ = BigInteger.valueOf(32); - private static String logString = "[{}]\t Op: [{}] Gas: [{}]\t Deep: [{}] Hint: [{}]"; + private static String logString = "[{}]\t Op: [{}] Gas: [{}] Deep: [{}] Hint: [{}]"; private static BigInteger MAX_GAS = BigInteger.valueOf(Long.MAX_VALUE); + /* Keeps track of the number of steps performed in this VM */ + private int vmCounter = 0; + public void step(Program program) { + program.fullTrace(); + try { OpCode op = OpCode.code(program.getCurrentOp()); program.setLastOp(op.val()); @@ -79,92 +84,96 @@ public class VM { Stack stack = program.getStack(); String hint = ""; + long callGas = 0, memWords = 0; // parameters for logging + long gasCost = GasCost.STEP; + long gasBefore = program.getGas().longValue(); int stepBefore = program.getPC(); - // Log debugging line for VM - if(program.getNumber().intValue() == CONFIG.dumpBlock()) - this.dumpLine(op.val(), program); - // Calculate fees and spend gas switch (op) { case STOP: case SUICIDE: - // The ops that doesn't charged by step, or - // charged in the following section + // The ops that don't charge by step + gasCost = GasCost.STOP; break; case SSTORE: // for gas calculations [YP 9.2] DataWord newValue = stack.get(stack.size()-2); DataWord oldValue = program.storageLoad(stack.peek()); if (oldValue == null && !newValue.isZero()) { - program.spendGas(GasCost.SSTORE * 2, op.name()); + gasCost = GasCost.SSTORE * 2; } else if (oldValue != null && newValue.isZero()) { - program.spendGas(GasCost.SSTORE * 0, op.name()); + gasCost = GasCost.SSTORE * 0; } else - program.spendGas(GasCost.SSTORE, op.name()); + gasCost = GasCost.SSTORE; break; case SLOAD: - program.spendGas(GasCost.SLOAD, op.name()); + gasCost = GasCost.SLOAD; break; case BALANCE: - program.spendGas(GasCost.BALANCE, op.name()); + gasCost = GasCost.BALANCE; break; // These all operate on memory and therefore potentially expand it: case MSTORE: newMemSize = stack.peek().value().add(BigInteger.valueOf(32)); - program.spendGas(GasCost.STEP, op.name()); break; case MSTORE8: newMemSize = stack.peek().value().add(BigInteger.ONE); - program.spendGas(GasCost.STEP, op.name()); break; case MLOAD: newMemSize = stack.peek().value().add(BigInteger.valueOf(32)); - program.spendGas(GasCost.STEP, op.name()); break; case RETURN: newMemSize = stack.peek().value().add(stack.get(stack.size()-2).value()); - program.spendGas(GasCost.STEP, op.name()); break; case SHA3: - program.spendGas(GasCost.SHA3, op.name()); + gasCost = GasCost.SHA3; newMemSize = stack.peek().value().add(stack.get(stack.size()-2).value()); break; case CALLDATACOPY: newMemSize = stack.peek().value().add(stack.get(stack.size()-3).value()); - program.spendGas(GasCost.STEP, op.name()); break; case CODECOPY: newMemSize = stack.peek().value().add(stack.get(stack.size()-3).value()); - program.spendGas(GasCost.STEP, op.name()); break; case CALL: - program.spendGas(GasCost.CALL, op.name()); - BigInteger callGas = stack.get(stack.size()-1).value(); - if(callGas.compareTo(program.getGas().value()) == 1) { + gasCost = GasCost.CALL; + BigInteger callGasWord = stack.get(stack.size()-1).value(); + if(callGasWord.compareTo(program.getGas().value()) == 1) { throw program.new OutOfGasException(); } + callGas = callGasWord.longValue(); // Casting to long (causing overflow) as workaround for PoC5 - should be removed for PoC6 long x = stack.get(stack.size()-6).value().add(stack.get(stack.size()-7).value()).longValue(); long y = stack.get(stack.size()-4).value().add(stack.get(stack.size()-5).value()).longValue(); newMemSize = BigInteger.valueOf(Math.max(x, y)); break; case CREATE: - program.spendGas(GasCost.CREATE, op.name()); + gasCost = GasCost.CREATE; newMemSize = stack.get(stack.size()-2).value().add(stack.get(stack.size()-3).value()); break; default: - program.spendGas(GasCost.STEP, op.name()); break; } + program.spendGas(gasCost, op.name()); + if(newMemSize.compareTo(MAX_GAS) == 1) { throw program.new OutOfGasException(); } - // memory gas calc - long memoryUsage = (newMemSize.longValue() + 31) / 32 * 32; - if (memoryUsage > oldMemSize) - program.spendGas(GasCost.MEMORY * ((memoryUsage - oldMemSize) / 32), op.name() + " (memory usage)"); + // memory gas calc + long memoryUsage = (newMemSize.longValue() + 31) / 32 * 32; + if (memoryUsage > oldMemSize) { + memWords = (memoryUsage - oldMemSize) / 32; + long memGas = GasCost.MEMORY * memWords; + program.spendGas(memGas, op.name() + " (memory usage)"); + gasCost += memGas; + } + + // Log debugging line for VM + if(program.getNumber().intValue() == CONFIG.dumpBlock()) + this.dumpLine(op, gasBefore, gasCost+callGas, memWords, program); + // Execute operation switch (op) { /** @@ -492,7 +501,7 @@ public class VM { DataWord callValue = program.getCallValue(); if (logger.isInfoEnabled()) - hint = "value: " + callValue.toString(); + hint = "value: " + callValue; program.stackPush(callValue); program.step(); @@ -502,7 +511,7 @@ public class VM { DataWord value = program.getDataValue(dataOffs); if (logger.isInfoEnabled()) - hint = "data: " + value.toString(); + hint = "data: " + value; program.stackPush(value); program.step(); @@ -677,7 +686,7 @@ public class VM { DataWord val = program.storageLoad(key); if (logger.isInfoEnabled()) - hint = "key: " + key + " value: " + val; + hint = "key: " + key + " value: " + val; if (val == null) { val = key.and(DataWord.ZERO); @@ -784,12 +793,17 @@ public class VM { DataWord outDataOffs = program.stackPop(); DataWord outDataSize = program.stackPop(); - - if (logger.isInfoEnabled()) + + if (logger.isInfoEnabled()) { + hint = "addr: " + Hex.toHexString(toAddress.getLast20Bytes()) + + " gas: " + gas.shortHex() + + " inOff: " + inDataOffs.shortHex() + + " inSize: " + inDataSize.shortHex(); logger.info(logString, program.getPC(), String.format("%-12s", op.name()), program.getGas().value(), program.invokeData.getCallDeep(), hint); + } program.callToAddress(gas, toAddress, value, inDataOffs, inDataSize, outDataOffs, outDataSize); @@ -803,7 +817,9 @@ public class VM { program.setHReturn(hReturn); if (logger.isInfoEnabled()) - hint = "data: " + Hex.toHexString(hReturn.array()); + hint = "data: " + Hex.toHexString(hReturn.array()) + + " offset: " + offset.value() + + " size: " + size.value(); program.step(); program.stop(); @@ -818,18 +834,17 @@ public class VM { program.stop(); } break; default:{ + throw new IllegalOperationException(); } } if (logger.isInfoEnabled() && !op.equals(CALL) && !op.equals(CREATE)) - logger.info(logString, stepBefore, String.format("%-12s", op.name()), program.getGas().longValue(), + logger.info(logString, stepBefore, String.format("%-12s", + op.name()), program.getGas().longValue(), program.invokeData.getCallDeep(), hint); - - program.fullTrace(); - - -// program.fullTrace(); + + vmCounter++; } catch (RuntimeException e) { if(e instanceof OutOfGasException) logger.warn("OutOfGasException occurred", e); @@ -858,28 +873,69 @@ public class VM { } } - private void dumpLine(byte op, Program program) { - switch (OpCode.code(op)) { - case STOP: case RETURN: case SUICIDE: - - ContractDetails details = program.getResult().getRepository() + /* + * Dumping the VM state at the current operation in various styles + * - standard Not Yet Implemented + * - standard+ (owner address, program counter, operation, gas left) + * - pretty (stack, memory, storage, level, contract, + * vmCounter, internalSteps, operation + gasBefore, gasCost, memWords) + */ + private void dumpLine(OpCode op, long gasBefore, long gasCost, long memWords, Program program) { + if(CONFIG.dumpStyle().equals("standard+")) { + switch (op) { + case STOP: case RETURN: case SUICIDE: + + ContractDetails details = program.getResult().getRepository() + .getContractDetails(program.getOwnerAddress().getLast20Bytes()); + List storageKeys = new ArrayList<>(details.getStorage().keySet()); + Collections.sort((List) storageKeys); + + for (DataWord key : storageKeys) { + dumpLogger.trace("{} {}", + Hex.toHexString(key.getNoLeadZeroesData()), + Hex.toHexString(details.getStorage().get(key).getNoLeadZeroesData())); + } + default: + break; + } + String addressString = Hex.toHexString(program.getOwnerAddress().getLast20Bytes()); + String pcString = Hex.toHexString(new DataWord(program.getPC()).getNoLeadZeroesData()); + String opString = Hex.toHexString(new byte[]{op.val()}); + String gasString = Hex.toHexString(program.getGas().getNoLeadZeroesData()); + + dumpLogger.trace("{} {} {} {}", addressString, pcString, opString, gasString); + } else if(CONFIG.dumpStyle().equals("pretty")) { + dumpLogger.trace(" STACK"); + for (DataWord item : program.getStack()) { + dumpLogger.trace("{}", item); + } + dumpLogger.trace(" MEMORY"); + String memoryString = program.memoryToString(); + if(!"".equals(memoryString)) + dumpLogger.trace("{}", memoryString); + + dumpLogger.trace(" STORAGE"); + ContractDetails details = program.getResult().getRepository() .getContractDetails(program.getOwnerAddress().getLast20Bytes()); - List storageKeys = new ArrayList<>(details.getStorage().keySet()); - Collections.sort((List) storageKeys); - - for (DataWord key : storageKeys) { - dumpLogger.info("{} {}", - Hex.toHexString(key.getNoLeadZeroesData()), - Hex.toHexString(details.getStorage().get(key).getNoLeadZeroesData())); - } - default: - break; - } - String addressString = Hex.toHexString(program.getOwnerAddress().getLast20Bytes()); - String pcString = Hex.toHexString(new DataWord(program.getPC()).getNoLeadZeroesData()); - String opString = Hex.toHexString(new byte[]{op}); - String gasString = Hex.toHexString(program.getGas().getNoLeadZeroesData()); - - dumpLogger.info("{} {} {} {}", addressString, pcString, opString, gasString); + List storageKeys = new ArrayList<>(details.getStorage().keySet()); + Collections.sort((List) storageKeys); + + for (DataWord key : storageKeys) { + dumpLogger.trace("{}: {}", + key.shortHex(), + details.getStorage().get(key).shortHex()); + } + + int level = program.invokeData.getCallDeep(); + String contract = Hex.toHexString(program.getOwnerAddress().getLast20Bytes()); + String internalSteps = String.format("%4s", Integer.toHexString(program.getPC())).replace(' ', '0').toUpperCase(); + dumpLogger.trace("{} | {} | #{} | {} : {} | {} | -{} | {}x32", + level, contract, vmCounter, internalSteps, op, + gasBefore, gasCost, memWords); + } } + + @SuppressWarnings("serial") + private class IllegalOperationException extends RuntimeException {} } diff --git a/ethereumj-core/src/main/resources/log4j-detailed.properties b/ethereumj-core/src/main/resources/log4j-detailed.properties index 6c2a9e2a..6042cb8a 100644 --- a/ethereumj-core/src/main/resources/log4j-detailed.properties +++ b/ethereumj-core/src/main/resources/log4j-detailed.properties @@ -22,7 +22,7 @@ log4j.logger.java.nio = ERROR log4j.logger.io.netty = ERROR log4j.logger.wire = ERROR log4j.logger.wallet = ERROR -log4j.logger.VM = DEBUG +log4j.logger.VM = TRACE log4j.logger.dump = OFF log4j.logger.main = INFO log4j.logger.trie = ERROR diff --git a/ethereumj-core/src/main/resources/log4j.properties b/ethereumj-core/src/main/resources/log4j.properties index 6da7d683..065d1bdb 100644 --- a/ethereumj-core/src/main/resources/log4j.properties +++ b/ethereumj-core/src/main/resources/log4j.properties @@ -1,11 +1,18 @@ # Root logger option log4j.rootLogger=DEBUG, stdout, file +log4j.logger.dump=TRACE, DUMP # Direct log messages to stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern= %d{HH:mm:ss} [%c{1}] %m%n +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} [%c{1}] %m%n +log4j.appender.stdout.Threshold=DEBUG + +# Direct log messages to stdout +log4j.appender.DUMP=org.apache.log4j.ConsoleAppender +log4j.appender.DUMP.Target=System.out +log4j.appender.DUMP.layout=org.apache.log4j.PatternLayout log4j.appender.file=org.apache.log4j.rolling.RollingFileAppender log4j.appender.file.layout=org.apache.log4j.PatternLayout @@ -14,7 +21,7 @@ log4j.appender.file.RollingPolicy=org.apache.log4j.rolling.TimeBasedRollingPolic log4j.appender.file.RollingPolicy.FileNamePattern=./logs/ethereum_%d{yyyy-MM-dd}_h%d{HH}.log # filter noisy classes -log4j.logger.org.ethereum.core = ERROR +log4j.logger.block = ERROR log4j.logger.wallet = ERROR log4j.logger.net = ERROR log4j.logger.db = ERROR diff --git a/ethereumj-core/src/main/resources/system.properties b/ethereumj-core/src/main/resources/system.properties index 293be2ae..747d0db8 100644 --- a/ethereumj-core/src/main/resources/system.properties +++ b/ethereumj-core/src/main/resources/system.properties @@ -11,7 +11,7 @@ peer.discovery.ip.list = 185.43.109.23:30303, \ 54.72.69.180:30303, \ 54.201.28.117:30303, \ 70.29.74.8:7 - +#peer.discovery.ip.list = 127.0.0.1:30303 # active peer ip and port # that is the peer through @@ -32,6 +32,10 @@ peer.discovery.ip.list = 185.43.109.23:30303, \ peer.active.ip = 185.43.109.23 peer.active.port = 30303 +# Localhost +#peer.active.ip = 127.0.0.1 +#peer.active.port = 30303 + #peer.active.ip = 54.72.69.180 #peer.active.port = 30303 @@ -96,10 +100,16 @@ coinbase.secret = monkey # all the state will be dumped # in JSON form to [dump.dir] # if [dump.full] = true -# posible values [true/false] +# possible values [true/false] dump.full = false dump.dir = dmp + +# This defines the vmtrace dump +# to the console and the style +# -1 for no block trace +# styles: [pretty/standard+] (default: standard+) dump.block = -1 +dump.style = pretty # clean the dump dir each start dump.clean.on.restart = true diff --git a/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java b/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java index adaa64c3..69063c65 100644 --- a/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java @@ -6,22 +6,20 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.math.BigInteger; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Random; import org.ethereum.core.AccountState; -import org.ethereum.crypto.HashUtil; -import org.ethereum.db.ByteArrayWrapper; import org.ethereum.db.DatabaseImpl; -import org.ethereum.util.CompactEncoder; -import org.ethereum.util.Value; -import org.iq80.leveldb.DBIterator; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; @@ -520,53 +518,57 @@ public class TrieTest { trie.update("te", "testy"); assertEquals("8452568af70d8d140f58d941338542f645fcca50094b20f3c3d8c3df49337928", Hex.toHexString(trie.getRootHash())); } + + private final String randomDictionary = "spinneries, archipenko, prepotency, herniotomy, preexpress, relaxative, insolvably, debonnaire, apophysate, virtuality, cavalryman, utilizable, diagenesis, vitascopic, governessy, abranchial, cyanogenic, gratulated, signalment, predicable, subquality, crystalize, prosaicism, oenologist, repressive, impanelled, cockneyism, bordelaise, compigne, konstantin, predicated, unsublimed, hydrophane, phycomyces, capitalise, slippingly, untithable, unburnable, deoxidizer, misteacher, precorrect, disclaimer, solidified, neuraxitis, caravaning, betelgeuse, underprice, uninclosed, acrogynous, reirrigate, dazzlingly, chaffiness, corybantes, intumesced, intentness, superexert, abstrusely, astounding, pilgrimage, posttarsal, prayerless, nomologist, semibelted, frithstool, unstinging, ecalcarate, amputating, megascopic, graphalloy, platteland, adjacently, mingrelian, valentinus, appendical, unaccurate, coriaceous, waterworks, sympathize, doorkeeper, overguilty, flaggingly, admonitory, aeriferous, normocytic, parnellism, catafalque, odontiasis, apprentice, adulterous, mechanisma, wilderness, undivorced, reinterred, effleurage, pretrochal, phytogenic, swirlingly, herbarized, unresolved, classifier, diosmosing, microphage, consecrate, astarboard, predefying, predriving, lettergram, ungranular, overdozing, conferring, unfavorite, peacockish, coinciding, erythraeum, freeholder, zygophoric, imbitterer, centroidal, appendixes, grayfishes, enological, indiscreet, broadcloth, divulgated, anglophobe, stoopingly, bibliophil, laryngitis, separatist, estivating, bellarmine, greasiness, typhlology, xanthation, mortifying, endeavorer, aviatrices, unequalise, metastatic, leftwinger, apologizer, quatrefoil, nonfouling, bitartrate, outchiding, undeported, poussetted, haemolysis, asantehene, montgomery, unjoinable, cedarhurst, unfastener, nonvacuums, beauregard, animalized, polyphides, cannizzaro, gelatinoid, apologised, unscripted, tracheidal, subdiscoid, gravelling, variegated, interabang, inoperable, immortelle, laestrygon, duplicatus, proscience, deoxidised, manfulness, channelize, nondefense, ectomorphy, unimpelled, headwaiter, hexaemeric, derivation, prelexical, limitarian, nonionized, prorefugee, invariably, patronizer, paraplegia, redivision, occupative, unfaceable, hypomnesia, psalterium, doctorfish, gentlefolk, overrefine, heptastich, desirously, clarabelle, uneuphonic, autotelism, firewarden, timberjack, fumigation, drainpipes, spathulate, novelvelle, bicorporal, grisliness, unhesitant, supergiant, unpatented, womanpower, toastiness, multichord, paramnesia, undertrick, contrarily, neurogenic, gunmanship, settlement, brookville, gradualism, unossified, villanovan, ecospecies, organising, buckhannon, prefulfill, johnsonese, unforegone, unwrathful, dunderhead, erceldoune, unwadeable, refunction, understuff, swaggering, freckliest, telemachus, groundsill, outslidden, bolsheviks, recognizer, hemangioma, tarantella, muhammedan, talebearer, relocation, preemption, chachalaca, septuagint, ubiquitous, plexiglass, humoresque, biliverdin, tetraploid, capitoline, summerwood, undilating, undetested, meningitic, petrolatum, phytotoxic, adiphenine, flashlight, protectory, inwreathed, rawishness, tendrillar, hastefully, bananaquit, anarthrous, unbedimmed, herborized, decenniums, deprecated, karyotypic, squalidity, pomiferous, petroglyph, actinomere, peninsular, trigonally, androgenic, resistance, unassuming, frithstool, documental, eunuchised, interphone, thymbraeus, confirmand, expurgated, vegetation, myographic, plasmagene, spindrying, unlackeyed, foreknower, mythically, albescence, rebudgeted, implicitly, unmonastic, torricelli, mortarless, labialized, phenacaine, radiometry, sluggishly, understood, wiretapper, jacobitely, unbetrayed, stadholder, directress, emissaries, corelation, sensualize, uncurbable, permillage, tentacular, thriftless, demoralize, preimagine, iconoclast, acrobatism, firewarden, transpired, bluethroat, wanderjahr, groundable, pedestrian, unulcerous, preearthly, freelanced, sculleries, avengingly, visigothic, preharmony, bressummer, acceptable, unfoolable, predivider, overseeing, arcosolium, piriformis, needlecord, homebodies, sulphation, phantasmic, unsensible, unpackaged, isopiestic, cytophagic, butterlike, frizzliest, winklehawk, necrophile, mesothorax, cuchulainn, unrentable, untangible, unshifting, unfeasible, poetastric, extermined, gaillardia, nonpendent, harborside, pigsticker, infanthood, underrower, easterling, jockeyship, housebreak, horologium, undepicted, dysacousma, incurrable, editorship, unrelented, peritricha, interchaff, frothiness, underplant, proafrican, squareness, enigmatise, reconciled, nonnumeral, nonevident, hamantasch, victualing, watercolor, schrdinger, understand, butlerlike, hemiglobin, yankeeland"; @Test public void testMasiveUpdate(){ - - String randomDictionary = "spinneries, archipenko, prepotency, herniotomy, preexpress, relaxative, insolvably, debonnaire, apophysate, virtuality, cavalryman, utilizable, diagenesis, vitascopic, governessy, abranchial, cyanogenic, gratulated, signalment, predicable, subquality, crystalize, prosaicism, oenologist, repressive, impanelled, cockneyism, bordelaise, compigne, konstantin, predicated, unsublimed, hydrophane, phycomyces, capitalise, slippingly, untithable, unburnable, deoxidizer, misteacher, precorrect, disclaimer, solidified, neuraxitis, caravaning, betelgeuse, underprice, uninclosed, acrogynous, reirrigate, dazzlingly, chaffiness, corybantes, intumesced, intentness, superexert, abstrusely, astounding, pilgrimage, posttarsal, prayerless, nomologist, semibelted, frithstool, unstinging, ecalcarate, amputating, megascopic, graphalloy, platteland, adjacently, mingrelian, valentinus, appendical, unaccurate, coriaceous, waterworks, sympathize, doorkeeper, overguilty, flaggingly, admonitory, aeriferous, normocytic, parnellism, catafalque, odontiasis, apprentice, adulterous, mechanisma, wilderness, undivorced, reinterred, effleurage, pretrochal, phytogenic, swirlingly, herbarized, unresolved, classifier, diosmosing, microphage, consecrate, astarboard, predefying, predriving, lettergram, ungranular, overdozing, conferring, unfavorite, peacockish, coinciding, erythraeum, freeholder, zygophoric, imbitterer, centroidal, appendixes, grayfishes, enological, indiscreet, broadcloth, divulgated, anglophobe, stoopingly, bibliophil, laryngitis, separatist, estivating, bellarmine, greasiness, typhlology, xanthation, mortifying, endeavorer, aviatrices, unequalise, metastatic, leftwinger, apologizer, quatrefoil, nonfouling, bitartrate, outchiding, undeported, poussetted, haemolysis, asantehene, montgomery, unjoinable, cedarhurst, unfastener, nonvacuums, beauregard, animalized, polyphides, cannizzaro, gelatinoid, apologised, unscripted, tracheidal, subdiscoid, gravelling, variegated, interabang, inoperable, immortelle, laestrygon, duplicatus, proscience, deoxidised, manfulness, channelize, nondefense, ectomorphy, unimpelled, headwaiter, hexaemeric, derivation, prelexical, limitarian, nonionized, prorefugee, invariably, patronizer, paraplegia, redivision, occupative, unfaceable, hypomnesia, psalterium, doctorfish, gentlefolk, overrefine, heptastich, desirously, clarabelle, uneuphonic, autotelism, firewarden, timberjack, fumigation, drainpipes, spathulate, novelvelle, bicorporal, grisliness, unhesitant, supergiant, unpatented, womanpower, toastiness, multichord, paramnesia, undertrick, contrarily, neurogenic, gunmanship, settlement, brookville, gradualism, unossified, villanovan, ecospecies, organising, buckhannon, prefulfill, johnsonese, unforegone, unwrathful, dunderhead, erceldoune, unwadeable, refunction, understuff, swaggering, freckliest, telemachus, groundsill, outslidden, bolsheviks, recognizer, hemangioma, tarantella, muhammedan, talebearer, relocation, preemption, chachalaca, septuagint, ubiquitous, plexiglass, humoresque, biliverdin, tetraploid, capitoline, summerwood, undilating, undetested, meningitic, petrolatum, phytotoxic, adiphenine, flashlight, protectory, inwreathed, rawishness, tendrillar, hastefully, bananaquit, anarthrous, unbedimmed, herborized, decenniums, deprecated, karyotypic, squalidity, pomiferous, petroglyph, actinomere, peninsular, trigonally, androgenic, resistance, unassuming, frithstool, documental, eunuchised, interphone, thymbraeus, confirmand, expurgated, vegetation, myographic, plasmagene, spindrying, unlackeyed, foreknower, mythically, albescence, rebudgeted, implicitly, unmonastic, torricelli, mortarless, labialized, phenacaine, radiometry, sluggishly, understood, wiretapper, jacobitely, unbetrayed, stadholder, directress, emissaries, corelation, sensualize, uncurbable, permillage, tentacular, thriftless, demoralize, preimagine, iconoclast, acrobatism, firewarden, transpired, bluethroat, wanderjahr, groundable, pedestrian, unulcerous, preearthly, freelanced, sculleries, avengingly, visigothic, preharmony, bressummer, acceptable, unfoolable, predivider, overseeing, arcosolium, piriformis, needlecord, homebodies, sulphation, phantasmic, unsensible, unpackaged, isopiestic, cytophagic, butterlike, frizzliest, winklehawk, necrophile, mesothorax, cuchulainn, unrentable, untangible, unshifting, unfeasible, poetastric, extermined, gaillardia, nonpendent, harborside, pigsticker, infanthood, underrower, easterling, jockeyship, housebreak, horologium, undepicted, dysacousma, incurrable, editorship, unrelented, peritricha, interchaff, frothiness, underplant, proafrican, squareness, enigmatise, reconciled, nonnumeral, nonevident, hamantasch, victualing, watercolor, schrdinger, understand, butlerlike, hemiglobin, yankeeland"; - List randomWords = Arrays.asList(randomDictionary.split(",")); - HashMap testerMap = new HashMap<>(); - - Trie trie = new Trie(mockDb); - Random generator = new Random(); - - // Random insertion - for (int i = 0; i < 100000; ++i ){ - - int randomIndex1 = generator.nextInt(randomWords.size()); - int randomIndex2 = generator.nextInt(randomWords.size()); - - String word1 = randomWords.get(randomIndex1).trim(); - String word2 = randomWords.get(randomIndex2).trim(); - - trie.update(word1, word2); - testerMap.put(word1, word2); - } - - int half = testerMap.size() / 2; - for (int r = 0; r < half; ++r){ - - int randomIndex = generator.nextInt(randomWords.size()); - String word1 = randomWords.get(randomIndex).trim(); - - testerMap.remove(word1); - trie.delete(word1); - } - - trie.cleanCacheGarbage(); - trie.sync(); - - // Assert the result now - Iterator keys = testerMap.keySet().iterator(); - while (keys.hasNext()){ - - String mapWord1 = keys.next(); - String mapWord2 = testerMap.get(mapWord1); - String treeWord2 = new String(trie.get(mapWord1)); - - Assert.assertEquals(mapWord2, treeWord2); - } + boolean massiveUpdateTestEnabled = false; + + if(massiveUpdateTestEnabled) { + List randomWords = Arrays.asList(randomDictionary.split(",")); + HashMap testerMap = new HashMap<>(); + + Trie trie = new Trie(mockDb); + Random generator = new Random(); + + // Random insertion + for (int i = 0; i < 100000; ++i ){ + + int randomIndex1 = generator.nextInt(randomWords.size()); + int randomIndex2 = generator.nextInt(randomWords.size()); + + String word1 = randomWords.get(randomIndex1).trim(); + String word2 = randomWords.get(randomIndex2).trim(); + + trie.update(word1, word2); + testerMap.put(word1, word2); + } + + int half = testerMap.size() / 2; + for (int r = 0; r < half; ++r){ + + int randomIndex = generator.nextInt(randomWords.size()); + String word1 = randomWords.get(randomIndex).trim(); + + testerMap.remove(word1); + trie.delete(word1); + } + + trie.cleanCacheGarbage(); + trie.sync(); + + // Assert the result now + Iterator keys = testerMap.keySet().iterator(); + while (keys.hasNext()){ + + String mapWord1 = keys.next(); + String mapWord2 = testerMap.get(mapWord1); + String treeWord2 = new String(trie.get(mapWord1)); + + Assert.assertEquals(mapWord2, treeWord2); + } + } } @@ -636,56 +638,58 @@ public class TrieTest { assertEquals(trieSingle.getRootHash(), trie2.getRootHash()); } - + @Test // tests saving keys to the file // public void testMasiveUpdateFromDB(){ - - String randomDictionary = "spinneries, archipenko, prepotency, herniotomy, preexpress, relaxative, insolvably, debonnaire, apophysate, virtuality, cavalryman, utilizable, diagenesis, vitascopic, governessy, abranchial, cyanogenic, gratulated, signalment, predicable, subquality, crystalize, prosaicism, oenologist, repressive, impanelled, cockneyism, bordelaise, compigne, konstantin, predicated, unsublimed, hydrophane, phycomyces, capitalise, slippingly, untithable, unburnable, deoxidizer, misteacher, precorrect, disclaimer, solidified, neuraxitis, caravaning, betelgeuse, underprice, uninclosed, acrogynous, reirrigate, dazzlingly, chaffiness, corybantes, intumesced, intentness, superexert, abstrusely, astounding, pilgrimage, posttarsal, prayerless, nomologist, semibelted, frithstool, unstinging, ecalcarate, amputating, megascopic, graphalloy, platteland, adjacently, mingrelian, valentinus, appendical, unaccurate, coriaceous, waterworks, sympathize, doorkeeper, overguilty, flaggingly, admonitory, aeriferous, normocytic, parnellism, catafalque, odontiasis, apprentice, adulterous, mechanisma, wilderness, undivorced, reinterred, effleurage, pretrochal, phytogenic, swirlingly, herbarized, unresolved, classifier, diosmosing, microphage, consecrate, astarboard, predefying, predriving, lettergram, ungranular, overdozing, conferring, unfavorite, peacockish, coinciding, erythraeum, freeholder, zygophoric, imbitterer, centroidal, appendixes, grayfishes, enological, indiscreet, broadcloth, divulgated, anglophobe, stoopingly, bibliophil, laryngitis, separatist, estivating, bellarmine, greasiness, typhlology, xanthation, mortifying, endeavorer, aviatrices, unequalise, metastatic, leftwinger, apologizer, quatrefoil, nonfouling, bitartrate, outchiding, undeported, poussetted, haemolysis, asantehene, montgomery, unjoinable, cedarhurst, unfastener, nonvacuums, beauregard, animalized, polyphides, cannizzaro, gelatinoid, apologised, unscripted, tracheidal, subdiscoid, gravelling, variegated, interabang, inoperable, immortelle, laestrygon, duplicatus, proscience, deoxidised, manfulness, channelize, nondefense, ectomorphy, unimpelled, headwaiter, hexaemeric, derivation, prelexical, limitarian, nonionized, prorefugee, invariably, patronizer, paraplegia, redivision, occupative, unfaceable, hypomnesia, psalterium, doctorfish, gentlefolk, overrefine, heptastich, desirously, clarabelle, uneuphonic, autotelism, firewarden, timberjack, fumigation, drainpipes, spathulate, novelvelle, bicorporal, grisliness, unhesitant, supergiant, unpatented, womanpower, toastiness, multichord, paramnesia, undertrick, contrarily, neurogenic, gunmanship, settlement, brookville, gradualism, unossified, villanovan, ecospecies, organising, buckhannon, prefulfill, johnsonese, unforegone, unwrathful, dunderhead, erceldoune, unwadeable, refunction, understuff, swaggering, freckliest, telemachus, groundsill, outslidden, bolsheviks, recognizer, hemangioma, tarantella, muhammedan, talebearer, relocation, preemption, chachalaca, septuagint, ubiquitous, plexiglass, humoresque, biliverdin, tetraploid, capitoline, summerwood, undilating, undetested, meningitic, petrolatum, phytotoxic, adiphenine, flashlight, protectory, inwreathed, rawishness, tendrillar, hastefully, bananaquit, anarthrous, unbedimmed, herborized, decenniums, deprecated, karyotypic, squalidity, pomiferous, petroglyph, actinomere, peninsular, trigonally, androgenic, resistance, unassuming, frithstool, documental, eunuchised, interphone, thymbraeus, confirmand, expurgated, vegetation, myographic, plasmagene, spindrying, unlackeyed, foreknower, mythically, albescence, rebudgeted, implicitly, unmonastic, torricelli, mortarless, labialized, phenacaine, radiometry, sluggishly, understood, wiretapper, jacobitely, unbetrayed, stadholder, directress, emissaries, corelation, sensualize, uncurbable, permillage, tentacular, thriftless, demoralize, preimagine, iconoclast, acrobatism, firewarden, transpired, bluethroat, wanderjahr, groundable, pedestrian, unulcerous, preearthly, freelanced, sculleries, avengingly, visigothic, preharmony, bressummer, acceptable, unfoolable, predivider, overseeing, arcosolium, piriformis, needlecord, homebodies, sulphation, phantasmic, unsensible, unpackaged, isopiestic, cytophagic, butterlike, frizzliest, winklehawk, necrophile, mesothorax, cuchulainn, unrentable, untangible, unshifting, unfeasible, poetastric, extermined, gaillardia, nonpendent, harborside, pigsticker, infanthood, underrower, easterling, jockeyship, housebreak, horologium, undepicted, dysacousma, incurrable, editorship, unrelented, peritricha, interchaff, frothiness, underplant, proafrican, squareness, enigmatise, reconciled, nonnumeral, nonevident, hamantasch, victualing, watercolor, schrdinger, understand, butlerlike, hemiglobin, yankeeland"; - List randomWords = Arrays.asList(randomDictionary.split(",")); - HashMap testerMap = new HashMap<>(); - - Trie trie = new Trie(mockDb); - Random generator = new Random(); - - // Random insertion - for (int i = 0; i < 50000; ++i ){ - - int randomIndex1 = generator.nextInt(randomWords.size()); - int randomIndex2 = generator.nextInt(randomWords.size()); - - String word1 = randomWords.get(randomIndex1).trim(); - String word2 = randomWords.get(randomIndex2).trim(); - - trie.update(word1, word2); - testerMap.put(word1, word2); - } - - trie.cleanCacheGarbage(); - trie.sync(); - - // Assert the result now - Iterator keys = testerMap.keySet().iterator(); - while (keys.hasNext()){ - - String mapWord1 = keys.next(); - String mapWord2 = testerMap.get(mapWord1); - String treeWord2 = new String(trie.get(mapWord1)); - - Assert.assertEquals(mapWord2, treeWord2); - } - - Trie trie2 = new Trie(mockDb, trie.getRootHash()); - - // Assert the result now - keys = testerMap.keySet().iterator(); - while (keys.hasNext()){ - - String mapWord1 = keys.next(); - String mapWord2 = testerMap.get(mapWord1); - String treeWord2 = new String(trie2.get(mapWord1)); - - Assert.assertEquals(mapWord2, treeWord2); - } + boolean massiveUpdateFromDBEnabled = false; + + if(massiveUpdateFromDBEnabled) { + List randomWords = Arrays.asList(randomDictionary.split(",")); + HashMap testerMap = new HashMap<>(); + + Trie trie = new Trie(mockDb); + Random generator = new Random(); + + // Random insertion + for (int i = 0; i < 50000; ++i ){ + + int randomIndex1 = generator.nextInt(randomWords.size()); + int randomIndex2 = generator.nextInt(randomWords.size()); + + String word1 = randomWords.get(randomIndex1).trim(); + String word2 = randomWords.get(randomIndex2).trim(); + + trie.update(word1, word2); + testerMap.put(word1, word2); + } + + trie.cleanCacheGarbage(); + trie.sync(); + + // Assert the result now + Iterator keys = testerMap.keySet().iterator(); + while (keys.hasNext()){ + + String mapWord1 = keys.next(); + String mapWord2 = testerMap.get(mapWord1); + String treeWord2 = new String(trie.get(mapWord1)); + + Assert.assertEquals(mapWord2, treeWord2); + } + + Trie trie2 = new Trie(mockDb, trie.getRootHash()); + + // Assert the result now + keys = testerMap.keySet().iterator(); + while (keys.hasNext()){ + + String mapWord1 = keys.next(); + String mapWord2 = testerMap.get(mapWord1); + String treeWord2 = new String(trie2.get(mapWord1)); + + Assert.assertEquals(mapWord2, treeWord2); + } + } } 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 30ecb5db..45d8915d 100644 --- a/ethereumj-core/src/test/java/org/ethereum/util/ByteUtilTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/util/ByteUtilTest.java @@ -98,6 +98,20 @@ public class ByteUtilTest { assertEquals(0, result); } + @Test + public void testNiceNiblesOutput_1(){ + byte[] test = {7, 0, 7, 5, 7, 0, 7, 0, 7, 9}; + String result = "\\x07\\x00\\x07\\x05\\x07\\x00\\x07\\x00\\x07\\x09"; + assertEquals(result, ByteUtil.nibblesToPrettyString(test)); + } + + @Test + public void testNiceNiblesOutput_2(){ + byte[] test = {7, 0, 7, 0xf, 7, 0, 0xa, 0, 7, 9}; + String result = "\\x07\\x00\\x07\\x0f\\x07\\x00\\x0a\\x00\\x07\\x09"; + assertEquals(result, ByteUtil.nibblesToPrettyString(test)); + } + @Test(expected=NullPointerException.class) public void testMatchingNibbleLength5() { // a == null diff --git a/ethereumj-core/src/test/java/org/ethereum/util/CompactEncoderTest.java b/ethereumj-core/src/test/java/org/ethereum/util/CompactEncoderTest.java index c8f7fa4a..6a41e1f3 100644 --- a/ethereumj-core/src/test/java/org/ethereum/util/CompactEncoderTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/util/CompactEncoderTest.java @@ -85,22 +85,4 @@ public class CompactEncoderTest { byte[] result = new byte[] { 7, 0, 7, 5, 7, 0, 7, 0, 7, 9, T }; assertArrayEquals(result, CompactEncoder.binToNibbles(test)); } - - - @Test - public void testNiceNiblesOutput_1(){ - byte[] test = {7, 0, 7, 5, 7, 0, 7, 0, 7, 9}; - String result = "\\x07\\x00\\x07\\x05\\x07\\x00\\x07\\x00\\x07\\x09"; - - assertEquals(result, CompactEncoder.nibblesToPrettyString(test)); - } - - @Test - public void testNiceNiblesOutput_2(){ - byte[] test = {7, 0, 7, 0xf, 7, 0, 0xa, 0, 7, 9}; - String result = "\\x07\\x00\\x07\\x0f\\x07\\x00\\x0a\\x00\\x07\\x09"; - - assertEquals(result, CompactEncoder.nibblesToPrettyString(test)); - } - }