diff --git a/.gitignore b/.gitignore index f20fc196..244ae990 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,8 @@ bin /src/main/java/samples /blockchain /state +/details +/logs *.db *.xlsx 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 a004f87d..789d547a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java @@ -4,6 +4,7 @@ import org.ethereum.db.DatabaseImpl; import org.ethereum.manager.WorldManager; import org.ethereum.net.message.StaticMessages; import org.ethereum.net.submit.WalletTransaction; +import org.ethereum.util.ByteUtil; import org.iq80.leveldb.DBIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,17 +56,15 @@ public class Blockchain { } public void setLastBlock(Block block){ - lastBlock = block; + this.lastBlock = block; } public int getSize(){ return index.size(); } - public Block getByNumber(int rowIndex){ - byte[] parentHash = index.get((long)rowIndex); - if (parentHash == null) return null; - return new Block(db.get(parentHash)); + public Block getByNumber(long rowIndex){ + return new Block(db.get(ByteUtil.longToBytes(rowIndex))); } public void addBlocks(List blocks) { @@ -91,7 +90,7 @@ public class Blockchain { for (int i = blocks.size() - 1; i >= 0 ; --i){ Block block = blocks.get(i); this.addBlock(block); - db.put(block.getParentHash(), block.getEncoded()); + db.put(ByteUtil.longToBytes(block.getNumber()), block.getEncoded()); if (logger.isDebugEnabled()) logger.debug("block added to the chain with hash: {}", Hex.toHexString(block.getHash())); } @@ -109,14 +108,12 @@ public class Blockchain { private void addBlock(Block block) { if(block.isValid()) { this.wallet.processBlock(block); - // that is the genesis case , we don't want to rely - // on this price will use default 10000000000000 - // todo: refactor this longValue some constant defaults class 10000000000000L + // In case of the genesis block we don't want to rely on the min gas price this.gasPrice = block.isGenesis() ? INITIAL_MIN_GAS_PRICE : block.getMinGasPrice(); - if(getLastBlock() == null || block.getNumber() > getLastBlock().getNumber()){ - setLastBlock( block ); - index.put(block.getNumber(), block.getParentHash()); - } + setLastBlock(block); + index.put(block.getNumber(), block.getParentHash()); + } else { + logger.warn("Invalid block with nr: " + block.getNumber()); } } @@ -163,29 +160,21 @@ public class Blockchain { try { if (index.size() == 0) { logger.info("DB is empty - adding Genesis"); - Block genesis = Genesis.getInstance(); - this.addBlock(genesis); - logger.debug("Block: " + genesis.getNumber() + " ---> " + genesis.toFlatString()); - db.put(genesis.getParentHash(), genesis.getEncoded()); + this.lastBlock = Genesis.getInstance(); + this.addBlock(lastBlock); + logger.debug("Block #" + Genesis.NUMBER + " -> " + lastBlock.toFlatString()); + db.put(ByteUtil.longToBytes(Genesis.NUMBER), lastBlock.getEncoded()); } logger.debug("Displaying blocks stored in DB sorted on blocknumber"); - byte[] parentHash = Genesis.PARENT_HASH; // get Genesis block by parentHash + long blockNr = Genesis.NUMBER; for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { - - byte[] parentRLP = db.get(parentHash); - if (parentRLP == null) return; - - Block block = new Block(parentRLP); - this.addBlock(block); - + this.lastBlock = new Block(db.get(ByteUtil.longToBytes(blockNr))); // in case of cold load play the contracts - WorldManager.instance.applyBlock(block); - - if (logger.isDebugEnabled()) - logger.debug("Block: " + getLastBlock().getNumber() + " ---> " + getLastBlock().toFlatString()); - parentHash = getLastBlock().getHash(); - + WorldManager.instance.applyBlock(lastBlock); + logger.debug("Block #" + lastBlock.getNumber() + " -> " + lastBlock.toFlatString()); + this.addBlock(lastBlock); + blockNr = lastBlock.getNumber()+1; } } finally { // Make sure you close the iterator to avoid resource leaks. diff --git a/ethereumj-core/src/main/java/org/ethereum/util/FastByteComparisons.java b/ethereumj-core/src/main/java/org/ethereum/util/FastByteComparisons.java new file mode 100644 index 00000000..da63110e --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/util/FastByteComparisons.java @@ -0,0 +1,217 @@ +package org.ethereum.util; + +import java.lang.reflect.Field; +import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import sun.misc.Unsafe; + +import com.google.common.primitives.Longs; +import com.google.common.primitives.UnsignedBytes; + +/** + * Utility code to do optimized byte-array comparison. + * This is borrowed and slightly modified from Guava's {@link UnsignedBytes} + * class to be able to compare arrays that start at non-zero offsets. + */ +public abstract class FastByteComparisons { + + /** + * Lexicographically compare two byte arrays. + */ + public static int compareTo(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { + return LexicographicalComparerHolder.BEST_COMPARER.compareTo( + b1, s1, l1, b2, s2, l2); + } + + private interface Comparer { + abstract public int compareTo(T buffer1, int offset1, int length1, + T buffer2, int offset2, int length2); + } + + private static Comparer lexicographicalComparerJavaImpl() { + return LexicographicalComparerHolder.PureJavaComparer.INSTANCE; + } + + + /** + * Provides a lexicographical comparer implementation; either a Java + * implementation or a faster implementation based on {@link Unsafe}. + * + *

Uses reflection to gracefully fall back to the Java implementation if + * {@code Unsafe} isn't available. + */ + private static class LexicographicalComparerHolder { + static final String UNSAFE_COMPARER_NAME = + LexicographicalComparerHolder.class.getName() + "$UnsafeComparer"; + + static final Comparer BEST_COMPARER = getBestComparer(); + /** + * Returns the Unsafe-using Comparer, or falls back to the pure-Java + * implementation if unable to do so. + */ + static Comparer getBestComparer() { + try { + Class theClass = Class.forName(UNSAFE_COMPARER_NAME); + + // yes, UnsafeComparer does implement Comparer + @SuppressWarnings("unchecked") + Comparer comparer = + (Comparer) theClass.getEnumConstants()[0]; + return comparer; + } catch (Throwable t) { // ensure we really catch *everything* + return lexicographicalComparerJavaImpl(); + } + } + + private enum PureJavaComparer implements Comparer { + INSTANCE; + + @Override + public int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + // Short circuit equal case + if (buffer1 == buffer2 && + offset1 == offset2 && + length1 == length2) { + return 0; + } + int end1 = offset1 + length1; + int end2 = offset2 + length2; + for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) { + int a = (buffer1[i] & 0xff); + int b = (buffer2[j] & 0xff); + if (a != b) { + return a - b; + } + } + return length1 - length2; + } + } + + @SuppressWarnings("unused") // used via reflection + private enum UnsafeComparer implements Comparer { + INSTANCE; + + static final Unsafe theUnsafe; + + /** The offset to the first element in a byte array. */ + static final int BYTE_ARRAY_BASE_OFFSET; + + static { + theUnsafe = (Unsafe) AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Object run() { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return f.get(null); + } catch (NoSuchFieldException e) { + // It doesn't matter what we throw; + // it's swallowed in getBestComparer(). + throw new Error(); + } catch (IllegalAccessException e) { + throw new Error(); + } + } + }); + + BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + + // sanity check - this should never fail + if (theUnsafe.arrayIndexScale(byte[].class) != 1) { + throw new AssertionError(); + } + } + + static final boolean littleEndian = + ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); + + /** + * Returns true if x1 is less than x2, when both values are treated as + * unsigned. + */ + static boolean lessThanUnsigned(long x1, long x2) { + return (x1 + Long.MIN_VALUE) < (x2 + Long.MIN_VALUE); + } + + /** + * Lexicographically compare two arrays. + * + * @param buffer1 left operand + * @param buffer2 right operand + * @param offset1 Where to start comparing in the left buffer + * @param offset2 Where to start comparing in the right buffer + * @param length1 How much to compare from the left buffer + * @param length2 How much to compare from the right buffer + * @return 0 if equal, < 0 if left is less than right, etc. + */ + @Override + public int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + // Short circuit equal case + if (buffer1 == buffer2 && + offset1 == offset2 && + length1 == length2) { + return 0; + } + int minLength = Math.min(length1, length2); + int minWords = minLength / Longs.BYTES; + int offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET; + int offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET; + + /* + * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a + * time is no slower than comparing 4 bytes at a time even on 32-bit. + * On the other hand, it is substantially faster on 64-bit. + */ + for (int i = 0; i < minWords * Longs.BYTES; i += Longs.BYTES) { + long lw = theUnsafe.getLong(buffer1, offset1Adj + (long) i); + long rw = theUnsafe.getLong(buffer2, offset2Adj + (long) i); + long diff = lw ^ rw; + + if (diff != 0) { + if (!littleEndian) { + return lessThanUnsigned(lw, rw) ? -1 : 1; + } + + // Use binary search + int n = 0; + int y; + int x = (int) diff; + if (x == 0) { + x = (int) (diff >>> 32); + n = 32; + } + + y = x << 16; + if (y == 0) { + n += 16; + } else { + x = y; + } + + y = x << 8; + if (y == 0) { + n += 8; + } + return (int) (((lw >>> n) & 0xFFL) - ((rw >>> n) & 0xFFL)); + } + } + + // The epilogue to cover the last (minLength % 8) elements. + for (int i = minWords * Longs.BYTES; i < minLength; i++) { + int result = UnsignedBytes.compare( + buffer1[offset1 + i], + buffer2[offset2 + i]); + if (result != 0) { + return result; + } + } + return length1 - length2; + } + } + } +} \ No newline at end of file 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 26613e8e..94c5d3dd 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -70,8 +70,6 @@ public class VM { case SLOAD: program.spendGas(GasCost.SLOAD, OpCode.code(op).name()); break; - case SSTORE: - break; case BALANCE: program.spendGas(GasCost.BALANCE, OpCode.code(op).name()); break; @@ -81,6 +79,10 @@ public class VM { case CALL: program.spendGas(GasCost.CALL, OpCode.code(op).name()); break; + case SSTORE: case STOP: + // The ops that doesn't charged by step, or charge + // in the following section + break; default: program.spendGas(GasCost.STEP, OpCode.code(op).name()); break; @@ -557,7 +559,7 @@ public class VM { int memoryUsage = (newMemSize - oldMemSize) /32; if (memoryUsage > 0) - program.spendGas(GasCost.MEMORY * memoryUsage, OpCode.code(op).name()); + program.spendGas(GasCost.MEMORY * memoryUsage, OpCode.code(op).name() + " (memory usage)"); program.fullTrace(); } catch (RuntimeException e) {