Light fix:
+ blockchain by number fix + stop is not charged fix
This commit is contained in:
parent
739b1425ef
commit
ca2d542a28
|
@ -20,6 +20,8 @@ bin
|
||||||
/src/main/java/samples
|
/src/main/java/samples
|
||||||
/blockchain
|
/blockchain
|
||||||
/state
|
/state
|
||||||
|
/details
|
||||||
|
/logs
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
*.xlsx
|
*.xlsx
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.ethereum.db.DatabaseImpl;
|
||||||
import org.ethereum.manager.WorldManager;
|
import org.ethereum.manager.WorldManager;
|
||||||
import org.ethereum.net.message.StaticMessages;
|
import org.ethereum.net.message.StaticMessages;
|
||||||
import org.ethereum.net.submit.WalletTransaction;
|
import org.ethereum.net.submit.WalletTransaction;
|
||||||
|
import org.ethereum.util.ByteUtil;
|
||||||
import org.iq80.leveldb.DBIterator;
|
import org.iq80.leveldb.DBIterator;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -55,17 +56,15 @@ public class Blockchain {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLastBlock(Block block){
|
public void setLastBlock(Block block){
|
||||||
lastBlock = block;
|
this.lastBlock = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize(){
|
public int getSize(){
|
||||||
return index.size();
|
return index.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block getByNumber(int rowIndex){
|
public Block getByNumber(long rowIndex){
|
||||||
byte[] parentHash = index.get((long)rowIndex);
|
return new Block(db.get(ByteUtil.longToBytes(rowIndex)));
|
||||||
if (parentHash == null) return null;
|
|
||||||
return new Block(db.get(parentHash));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBlocks(List<Block> blocks) {
|
public void addBlocks(List<Block> blocks) {
|
||||||
|
@ -91,7 +90,7 @@ public class Blockchain {
|
||||||
for (int i = blocks.size() - 1; i >= 0 ; --i){
|
for (int i = blocks.size() - 1; i >= 0 ; --i){
|
||||||
Block block = blocks.get(i);
|
Block block = blocks.get(i);
|
||||||
this.addBlock(block);
|
this.addBlock(block);
|
||||||
db.put(block.getParentHash(), block.getEncoded());
|
db.put(ByteUtil.longToBytes(block.getNumber()), block.getEncoded());
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
logger.debug("block added to the chain with hash: {}", Hex.toHexString(block.getHash()));
|
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) {
|
private void addBlock(Block block) {
|
||||||
if(block.isValid()) {
|
if(block.isValid()) {
|
||||||
this.wallet.processBlock(block);
|
this.wallet.processBlock(block);
|
||||||
// that is the genesis case , we don't want to rely
|
// In case of the genesis block we don't want to rely on the min gas price
|
||||||
// on this price will use default 10000000000000
|
|
||||||
// todo: refactor this longValue some constant defaults class 10000000000000L
|
|
||||||
this.gasPrice = block.isGenesis() ? INITIAL_MIN_GAS_PRICE : block.getMinGasPrice();
|
this.gasPrice = block.isGenesis() ? INITIAL_MIN_GAS_PRICE : block.getMinGasPrice();
|
||||||
if(getLastBlock() == null || block.getNumber() > getLastBlock().getNumber()){
|
setLastBlock(block);
|
||||||
setLastBlock( block );
|
index.put(block.getNumber(), block.getParentHash());
|
||||||
index.put(block.getNumber(), block.getParentHash());
|
} else {
|
||||||
}
|
logger.warn("Invalid block with nr: " + block.getNumber());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,29 +160,21 @@ public class Blockchain {
|
||||||
try {
|
try {
|
||||||
if (index.size() == 0) {
|
if (index.size() == 0) {
|
||||||
logger.info("DB is empty - adding Genesis");
|
logger.info("DB is empty - adding Genesis");
|
||||||
Block genesis = Genesis.getInstance();
|
this.lastBlock = Genesis.getInstance();
|
||||||
this.addBlock(genesis);
|
this.addBlock(lastBlock);
|
||||||
logger.debug("Block: " + genesis.getNumber() + " ---> " + genesis.toFlatString());
|
logger.debug("Block #" + Genesis.NUMBER + " -> " + lastBlock.toFlatString());
|
||||||
db.put(genesis.getParentHash(), genesis.getEncoded());
|
db.put(ByteUtil.longToBytes(Genesis.NUMBER), lastBlock.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Displaying blocks stored in DB sorted on blocknumber");
|
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()) {
|
for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) {
|
||||||
|
this.lastBlock = new Block(db.get(ByteUtil.longToBytes(blockNr)));
|
||||||
byte[] parentRLP = db.get(parentHash);
|
|
||||||
if (parentRLP == null) return;
|
|
||||||
|
|
||||||
Block block = new Block(parentRLP);
|
|
||||||
this.addBlock(block);
|
|
||||||
|
|
||||||
// in case of cold load play the contracts
|
// in case of cold load play the contracts
|
||||||
WorldManager.instance.applyBlock(block);
|
WorldManager.instance.applyBlock(lastBlock);
|
||||||
|
logger.debug("Block #" + lastBlock.getNumber() + " -> " + lastBlock.toFlatString());
|
||||||
if (logger.isDebugEnabled())
|
this.addBlock(lastBlock);
|
||||||
logger.debug("Block: " + getLastBlock().getNumber() + " ---> " + getLastBlock().toFlatString());
|
blockNr = lastBlock.getNumber()+1;
|
||||||
parentHash = getLastBlock().getHash();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Make sure you close the iterator to avoid resource leaks.
|
// Make sure you close the iterator to avoid resource leaks.
|
||||||
|
|
|
@ -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<T> {
|
||||||
|
abstract public int compareTo(T buffer1, int offset1, int length1,
|
||||||
|
T buffer2, int offset2, int length2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Comparer<byte[]> lexicographicalComparerJavaImpl() {
|
||||||
|
return LexicographicalComparerHolder.PureJavaComparer.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a lexicographical comparer implementation; either a Java
|
||||||
|
* implementation or a faster implementation based on {@link Unsafe}.
|
||||||
|
*
|
||||||
|
* <p>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<byte[]> BEST_COMPARER = getBestComparer();
|
||||||
|
/**
|
||||||
|
* Returns the Unsafe-using Comparer, or falls back to the pure-Java
|
||||||
|
* implementation if unable to do so.
|
||||||
|
*/
|
||||||
|
static Comparer<byte[]> getBestComparer() {
|
||||||
|
try {
|
||||||
|
Class<?> theClass = Class.forName(UNSAFE_COMPARER_NAME);
|
||||||
|
|
||||||
|
// yes, UnsafeComparer does implement Comparer<byte[]>
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Comparer<byte[]> comparer =
|
||||||
|
(Comparer<byte[]>) theClass.getEnumConstants()[0];
|
||||||
|
return comparer;
|
||||||
|
} catch (Throwable t) { // ensure we really catch *everything*
|
||||||
|
return lexicographicalComparerJavaImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum PureJavaComparer implements Comparer<byte[]> {
|
||||||
|
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<byte[]> {
|
||||||
|
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<Object>() {
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -70,8 +70,6 @@ public class VM {
|
||||||
case SLOAD:
|
case SLOAD:
|
||||||
program.spendGas(GasCost.SLOAD, OpCode.code(op).name());
|
program.spendGas(GasCost.SLOAD, OpCode.code(op).name());
|
||||||
break;
|
break;
|
||||||
case SSTORE:
|
|
||||||
break;
|
|
||||||
case BALANCE:
|
case BALANCE:
|
||||||
program.spendGas(GasCost.BALANCE, OpCode.code(op).name());
|
program.spendGas(GasCost.BALANCE, OpCode.code(op).name());
|
||||||
break;
|
break;
|
||||||
|
@ -81,6 +79,10 @@ public class VM {
|
||||||
case CALL:
|
case CALL:
|
||||||
program.spendGas(GasCost.CALL, OpCode.code(op).name());
|
program.spendGas(GasCost.CALL, OpCode.code(op).name());
|
||||||
break;
|
break;
|
||||||
|
case SSTORE: case STOP:
|
||||||
|
// The ops that doesn't charged by step, or charge
|
||||||
|
// in the following section
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
program.spendGas(GasCost.STEP, OpCode.code(op).name());
|
program.spendGas(GasCost.STEP, OpCode.code(op).name());
|
||||||
break;
|
break;
|
||||||
|
@ -557,7 +559,7 @@ public class VM {
|
||||||
int memoryUsage = (newMemSize - oldMemSize) /32;
|
int memoryUsage = (newMemSize - oldMemSize) /32;
|
||||||
|
|
||||||
if (memoryUsage > 0)
|
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();
|
program.fullTrace();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
|
|
Loading…
Reference in New Issue