Fixing consensus bugs:

+ clone for invoke data (if not changed unsafe changes will corrupt the env)
+ tx got into sign byte problem
+ significant improve over VM short tracing
This commit is contained in:
romanman 2014-07-18 19:30:29 +03:00
parent f7d2c80eba
commit 2be0364145
18 changed files with 429 additions and 93 deletions

View File

@ -31,6 +31,7 @@ public class SystemProperties {
private static Boolean DEFAULT_DUMP_FULL = false;
private static String DEFAULT_DUMP_DIR = "dmp";
private static Boolean DEFAULT_DUMP_CLEAN_ON_RESTART = true;
private static int DEFAULT_TRACE_STARTBLOCK = -1;
public static SystemProperties CONFIG = new SystemProperties();
private Properties prop = new Properties();
@ -132,6 +133,12 @@ public class SystemProperties {
return Integer.parseInt(prop.getProperty("active.peer.channel.timeout"));
}
public Integer traceStartBlock() {
if(prop.isEmpty()) return DEFAULT_TRACE_STARTBLOCK;
return Integer.parseInt(prop.getProperty("trace.startblock"));
}
public Boolean dumpFull() {
if(prop.isEmpty()) return DEFAULT_DUMP_FULL;
return Boolean.parseBoolean(prop.getProperty("dump.full"));

View File

@ -1,5 +1,7 @@
package org.ethereum.core;
import static org.ethereum.config.SystemProperties.CONFIG;
import org.apache.log4j.PropertyConfigurator;
import org.ethereum.db.DatabaseImpl;
import org.ethereum.manager.WorldManager;
import org.ethereum.util.ByteUtil;
@ -9,9 +11,13 @@ import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.IOException;
import java.util.*;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.ethereum.core.Denomination.*;
import static org.ethereum.core.Denomination.SZABO;
/**
* The Ethereum blockchain is in many ways similar to the Bitcoin blockchain,
@ -103,16 +109,28 @@ public class Blockchain {
for (int i = blocks.size() - 1; i >= 0 ; --i) {
this.addBlock(blocks.get(i));
// here we can turn on the detail tracing in the middle of the chain
if (lastBlock.getNumber() >= CONFIG.traceStartBlock() && CONFIG.traceStartBlock() != -1) {
URL configFile = ClassLoader
.getSystemResource("log4j-detailed.properties");
PropertyConfigurator.configure(configFile);
}
long blockNum = blocks.get(i).getNumber();
/* Debug check to see if the state is still as expected */
if(logger.isWarnEnabled()) {
String blockStateRootHash = Hex.toHexString(blocks.get(i).getStateRoot());
String worldStateRootHash = Hex.toHexString(WorldManager.getInstance().getRepository().getWorldState().getRootHash());
if(!blockStateRootHash.equals(worldStateRootHash))
logger.warn("WARNING: STATE CONFLICT! block: {} worldstate {} mismatch", blockNum, worldStateRootHash);
}
if(!blockStateRootHash.equals(worldStateRootHash)){
logger.warn("WARNING: STATE CONFLICT! block: {} worldstate {} mismatch", blockNum, worldStateRootHash);
System.exit(-1);
}
}
}
// Remove all wallet transactions as they already approved by the net
for (Block block : blocks) {
for (Transaction tx : block.getTransactionsList()) {
@ -122,6 +140,16 @@ public class Blockchain {
}
}
logger.info("*** Block chain size: [ {} ]", this.getSize());
/*
if (lastBlock.getNumber() >= 30) {
System.out.println("** checkpoint **");
this.close();
WorldManager.getInstance().getRepository().close();
System.exit(1);
}
*/
}
public void addBlock(Block block) {

View File

@ -45,32 +45,24 @@ public class ContractDetails {
if (value.equals(DataWord.ZERO)){
storageTrie.delete(key.getData());
int index = storageKeys.indexOf(key);
if (index != -1) {
storageKeys.remove(index);
storageValues.remove(index);
}
this.rlpEncoded = null;
} else{
storageTrie.update(key.getData(), RLP.encodeElement( value.getNoLeadZeroesData() ));
int index = storageKeys.indexOf(key);
if (index != -1) {
storageKeys.remove(index);
storageValues.remove(index);
}
storageKeys.add(key);
storageValues.add(value);
this.rlpEncoded = null;
}
this.rlpEncoded = null;
}
public DataWord get(DataWord key) {
@ -101,6 +93,8 @@ public class ContractDetails {
}
public byte[] getStorageHash() {
getEncoded();
return storageTrie.getRootHash();
}
@ -154,6 +148,13 @@ public class ContractDetails {
DataWord value = storageValues.get(i);
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);

View File

@ -4,12 +4,14 @@ import org.codehaus.plexus.util.FileUtils;
import org.ethereum.core.AccountState;
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;
import org.ethereum.vm.DataWord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex;
import java.io.BufferedWriter;
@ -65,6 +67,16 @@ public class Repository {
accountStateDB = new TrackTrie(worldState);
}
public Repository(byte[] stateRoot) {
detailsDB = new DatabaseImpl("details");
contractDetailsDB = new TrackDatabase(detailsDB);
stateDB = new DatabaseImpl("state");
worldState = new Trie(stateDB.getDb());
worldState.setRoot(stateRoot);
accountStateDB = new TrackTrie(worldState);
}
private Repository(TrackTrie accountStateDB, TrackDatabase contractDetailsDB) {
this.accountStateDB = accountStateDB;
this.contractDetailsDB = contractDetailsDB;
@ -208,7 +220,7 @@ public class Repository {
this.validateAddress(addr);
AccountState state = getAccountState(addr);
ContractDetails details = getContractDetails(addr);
ContractDetails details = getContractDetails(addr);
if (state == null || details == null) return;
details.put(key, value);
@ -310,10 +322,13 @@ public class Repository {
String dir = CONFIG.dumpDir() + "/";
String fileName = blockNumber + ".dmp";
String fileName = "";
if (txHash != null)
fileName = String.format("%d_%d_%s.dmp",
blockNumber, txNumber, txHash.substring(0, 8));
else
fileName = String.format("%d_c.dmp", blockNumber);
File dumpFile = new File(System.getProperty("user.dir") + "/" + dir + fileName);
FileWriter fw = null;
@ -335,24 +350,29 @@ public class Repository {
AccountState state = getAccountState(keyBytes);
ContractDetails details = getContractDetails(keyBytes);
BigInteger nonce = state.getNonce();
BigInteger balance = state.getBalance();
BigInteger nonce = (state != null)? state.getNonce():null;
BigInteger balance = (state != null)? state.getBalance():null;
byte[] stateRoot = state.getStateRoot();
byte[] codeHash = state.getCodeHash();
byte[] stateRoot = (state != null)? state.getStateRoot():null;
byte[] codeHash = (state != null)? state.getCodeHash():null;
byte[] code = details.getCode();
Map<DataWord, DataWord> storage = details.getStorage();
String accountLine = JSONHelper.dumpLine(key.getData(),
nonce.toByteArray(),
balance.toByteArray(), stateRoot, codeHash, code, storage);
(nonce != null)? BigIntegers.asUnsignedByteArray(nonce) : null,
(nonce != null)? BigIntegers.asUnsignedByteArray(balance): null,
stateRoot, codeHash, code, storage);
bw.write(accountLine);
bw.write("\n");
// {address: x, nonce: n1, balance: b1, stateRoot: s1, codeHash: c1, code: c2, sotrage: [key: k1, value: v1, key:k2, value: v2 ] }
}
String rootHash = Hex.toHexString(WorldManager.getInstance().getRepository().getRootHash());
bw.write(
String.format(" => Global State Root: [ %s ]", rootHash)
);
} catch (IOException e) {
logger.error(e.getMessage(), e);
} finally {
@ -364,6 +384,11 @@ public class Repository {
}
public void close() {
if (worldState != null){
worldState.sync();
}
if (this.stateDB != null)
stateDB.close();
if (this.detailsDB != null)

View File

@ -69,4 +69,9 @@ public class TrackDatabase implements Database {
db.delete(key);
}
}
@Override
public void close(){
db.close();
}
}

View File

@ -39,8 +39,8 @@ public class JSONHelper {
JSONArray orderFields = new JSONArray();
orderFields.add("address: " + Hex.toHexString(address));
orderFields.add(" nonce: " + Hex.toHexString(nonce));
orderFields.add(" balance: " + new BigInteger(balance).toString());
orderFields.add(" nonce: " + (nonce == null ? "00" : Hex.toHexString(nonce)));
orderFields.add(" balance: " + (balance == null ? "00" : Hex.toHexString(balance).toString()));
orderFields.add(" stateRoot: " + (stateRoot == null ? "" : Hex.toHexString(stateRoot)));
orderFields.add(" codeHash: " + (codeHash == null ? "" : Hex.toHexString(codeHash)));
orderFields.add(" code: " + (code == null ? "" : Hex.toHexString(code)));

View File

@ -69,12 +69,12 @@ public class WorldManager {
public static WorldManager getInstance() {
if(instance == null) {
instance = new WorldManager();
instance.getBlockChain().load();
instance.blockchain.load();
}
return instance;
}
public void applyTransaction(Transaction tx, byte[] coinbase) {
public void applyTransaction(Block block, Transaction tx, byte[] coinbase) {
byte[] senderAddress = tx.getSender();
AccountState senderAccount = repository.getAccountState(senderAddress);
@ -88,7 +88,7 @@ public class WorldManager {
// 1. VALIDATE THE NONCE
BigInteger nonce = senderAccount.getNonce();
BigInteger txNonce = new BigInteger(tx.getNonce());
BigInteger txNonce = new BigInteger(1, tx.getNonce());
if (nonce.compareTo(txNonce) != 0) {
if (stateLogger.isWarnEnabled())
stateLogger.warn("Invalid nonce account.nonce={} tx.nonce={}",
@ -187,10 +187,10 @@ public class WorldManager {
// 5. CREATE OR EXECUTE PROGRAM
if (isContractCreation || code != null) {
Block lastBlock = blockchain.getLastBlock();
Block currBlock = (block == null) ? blockchain.getLastBlock() : block;
ProgramInvoke programInvoke = ProgramInvokeFactory
.createProgramInvoke(tx, lastBlock, trackRepository);
.createProgramInvoke(tx, currBlock, trackRepository);
VM vm = new VM();
Program program = new Program(code, programInvoke);
@ -283,7 +283,8 @@ public class WorldManager {
int i = 0;
for (Transaction tx : block.getTransactionsList()) {
applyTransaction(tx, block.getCoinbase());
logger.info("apply block: [ {} ] tx: [ {} ] ", block.getNumber(), i);
applyTransaction(block, tx, block.getCoinbase());
repository.dumpState(block.getNumber(), i,
Hex.toHexString(tx.getHash()));
++i;
@ -295,7 +296,11 @@ public class WorldManager {
repository.addBalance(block.getCoinbase(), Block.BLOCK_REWARD);
for (Block uncle : block.getUncleList()) {
repository.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
}
}
repository.dumpState(block.getNumber(), 0,
null);
}
/***********************************************************************

View File

@ -94,7 +94,7 @@ public class MessageQueue {
if (null != messageQueue.peek()){
MessageRoundtrip messageRoundtrip = messageQueue.peek();
if (messageRoundtrip.getRetryTimes() == 0 || messageRoundtrip.hasToRetry()){
if (messageRoundtrip.getRetryTimes() == 0 ) {// todo: retry logic || messageRoundtrip.hasToRetry()){
Message msg = messageRoundtrip.getMsg();
sendToWire(msg);

View File

@ -74,11 +74,13 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
if (lastPongTime == 0) lastPongTime = System.currentTimeMillis();
if (tearDown) this.cancel();
/* todo: temporary cancel ping rate test
long currTime = System.currentTimeMillis();
if (currTime - lastPongTime > 30000) {
logger.info("No ping answer for [30 sec]");
throw new RuntimeException("No ping return for 30 [sec]");
}
*/
sendPing();
}
@ -175,7 +177,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
List<Transaction> txList = transactionsMessage.getTransactions();
for(Transaction tx : txList)
WorldManager.getInstance().applyTransaction(tx, null);
WorldManager.getInstance().applyTransaction(null, tx, null);
logger.info(transactionsMessage.toString());
if (peerListener != null) peerListener.console(transactionsMessage.toString());

View File

@ -15,7 +15,7 @@ public class TrackTrie implements TrieFacade {
private TrieFacade trie;
private boolean trackingChanges;
private boolean trackingChanges = false;
private Map<ByteArrayWrapper, byte[]> changes;
private Map<ByteArrayWrapper, byte[]> deletes;

View File

@ -27,7 +27,7 @@ public class Program {
Stack<DataWord> stack = new Stack<DataWord>();
ByteBuffer memory = null;
byte[] programAddress;
DataWord programAddress;
ProgramResult result = new ProgramResult();
@ -47,7 +47,8 @@ public class Program {
this.invokeData = invokeData;
this.ops = ops;
this.programAddress = invokeData.getOwnerAddress().getLast20Bytes();
this.programAddress = invokeData.getOwnerAddress();
}
public byte getCurrentOp() {
@ -82,6 +83,7 @@ public class Program {
}
public void setPC(DataWord pc) {
this.pc = pc.value().intValue();
if (this.pc == ops.length) {
@ -111,6 +113,7 @@ public class Program {
}
public void step() {
++pc;
if (pc >= ops.length) stop();
}
@ -271,7 +274,7 @@ public class Program {
// [5] COOK THE INVOKE AND EXECUTE
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(this, new DataWord(newAddress), DataWord.ZERO,
new DataWord(gas), BigInteger.ZERO, null, trackRepository);
new DataWord(gas), BigInteger.ZERO, null, trackRepository, this.invokeData.getCallDeep() + 1);
VM vm = new VM();
Program program = new Program(programCode.array(), programInvoke);
@ -333,8 +336,8 @@ public class Program {
byte[] programCode = this.result.getRepository().getCode(toAddress);
if (logger.isInfoEnabled())
logger.info("calling for existing contract: address={}",
Hex.toHexString(toAddress));
logger.info("calling for existing contract: address: [ {} ], outDataOffs: [ {} ], outDataSize: [ {} ] ",
Hex.toHexString(toAddress), outDataOffs.longValue(), outDataSize.longValue());
byte[] senderAddress = this.getOwnerAddress().getLast20Bytes();
@ -380,7 +383,7 @@ public class Program {
ProgramInvokeFactory.createProgramInvoke(this, toAddressDW,
endowmentValue, gas, result.getRepository().getBalance(toAddress),
data.array(),
trackRepository);
trackRepository, this.invokeData.getCallDeep() + 1);
ProgramResult result = null;
@ -458,12 +461,12 @@ public class Program {
public void storageSave(byte[] key, byte[] val) {
DataWord keyWord = new DataWord(key);
DataWord valWord = new DataWord(val);
result.getRepository().addStorageRow(this.programAddress, keyWord, valWord);
result.getRepository().addStorageRow(this.programAddress.getLast20Bytes(), keyWord, valWord);
}
public DataWord getOwnerAddress() {
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getOwnerAddress();
return this.programAddress.clone();
}
public DataWord getBalance(DataWord address) {
@ -477,17 +480,17 @@ public class Program {
public DataWord getOriginAddress() {
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getOriginAddress();
return invokeData.getOriginAddress().clone();
}
public DataWord getCallerAddress() {
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getCallerAddress();
return invokeData.getCallerAddress().clone();
}
public DataWord getGasPrice() {
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getMinGasPrice();
return invokeData.getMinGasPrice().clone();
}
public DataWord getGas() {
@ -498,12 +501,12 @@ public class Program {
public DataWord getCallValue() {
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getCallValue();
return invokeData.getCallValue().clone();
}
public DataWord getDataSize() {
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getDataSize();
return invokeData.getDataSize().clone();
}
public DataWord getDataValue(DataWord index) {
@ -517,31 +520,31 @@ public class Program {
}
public DataWord storageLoad(DataWord key) {
return result.getRepository().getStorageValue(this.programAddress, key);
return result.getRepository().getStorageValue(this.programAddress.getLast20Bytes(), key);
}
public DataWord getPrevHash() {
return invokeData.getPrevHash();
return invokeData.getPrevHash().clone();
}
public DataWord getCoinbase() {
return invokeData.getCoinbase();
return invokeData.getCoinbase().clone();
}
public DataWord getTimestamp() {
return invokeData.getTimestamp();
return invokeData.getTimestamp().clone();
}
public DataWord getNumber() {
return invokeData.getNumber();
return invokeData.getNumber().clone();
}
public DataWord getDifficulty() {
return invokeData.getDifficulty();
return invokeData.getDifficulty().clone();
}
public DataWord getGaslimit() {
return invokeData.getGaslimit();
return invokeData.getGaslimit().clone();
}
@ -564,7 +567,8 @@ public class Program {
}
if (stackData.length() > 0) stackData.insert(0, "\n");
ContractDetails contractDetails = this.result.getRepository().getContractDetails(this.programAddress);
ContractDetails contractDetails = this.result.getRepository().
getContractDetails(this.programAddress.getLast20Bytes());
StringBuilder storageData = new StringBuilder();
for (DataWord key : contractDetails.getStorage().keySet()) {
storageData.append(" ").append(key).append(" -> ").

View File

@ -31,4 +31,6 @@ public interface ProgramInvoke {
public boolean byTransaction();
boolean byTestingSuite();
public int getCallDeep();
}

View File

@ -3,6 +3,7 @@ package org.ethereum.vm;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.db.Repository;
import org.ethereum.manager.WorldManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
@ -20,9 +21,10 @@ public class ProgramInvokeFactory {
private static Logger logger = LoggerFactory.getLogger("VM");
// Invocation by the wire tx
public static ProgramInvoke createProgramInvoke(Transaction tx, Block lastBlock, Repository repository) {
public static ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository) {
// https://ethereum.etherpad.mozilla.org/26
Block lastBlock = WorldManager.getInstance().getBlockChain().getLastBlock();
/*** ADDRESS op ***/
// YP: Get address of currently executing account.
@ -58,19 +60,19 @@ public class ProgramInvokeFactory {
byte[] lastHash = lastBlock.getHash();
/*** COINBASE op ***/
byte[] coinbase = lastBlock.getCoinbase();
byte[] coinbase = block.getCoinbase();
/*** TIMESTAMP op ***/
long timestamp = lastBlock.getTimestamp();
long timestamp = block.getTimestamp();
/*** NUMBER op ***/
long number = lastBlock.getNumber();
long number = block.getNumber();
/*** DIFFICULTY op ***/
byte[] difficulty = lastBlock.getDifficulty();
byte[] difficulty = block.getDifficulty();
/*** GASLIMIT op ***/
long gaslimit = lastBlock.getGasLimit();
long gaslimit = block.getGasLimit();
if (logger.isInfoEnabled()) {
logger.info("Program invocation: \n" +
@ -121,7 +123,7 @@ public class ProgramInvokeFactory {
public static ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
DataWord inValue, DataWord inGas,
BigInteger balanceInt, byte[] dataIn,
Repository repository) {
Repository repository, int callDeep) {
DataWord address = toAddress;
DataWord origin = program.getOriginAddress();
@ -175,6 +177,6 @@ public class ProgramInvokeFactory {
return new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue,
data, lastHash, coinbase, timestamp, number, difficulty, gasLimit,
repository);
repository, callDeep);
}
}

View File

@ -13,7 +13,8 @@ import java.util.Map;
public class ProgramInvokeImpl implements ProgramInvoke {
/*** TRANSACTION env ***/
private DataWord address, origin, caller,
private DataWord address;
private DataWord origin, caller,
balance, gas, gasPrice, callValue;
byte[] msgData;
@ -27,11 +28,12 @@ public class ProgramInvokeImpl implements ProgramInvoke {
private Repository repository;
private boolean byTransaction = true;
private boolean byTestingSuite = false;
private int callDeep = 0;
public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance,
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
DataWord lastHash, DataWord coinbase, DataWord timestamp, DataWord number, DataWord difficulty,
DataWord gaslimit, Repository repository) {
DataWord gaslimit, Repository repository, int callDeep) {
// Transaction env
this.address = address;
@ -53,6 +55,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
this.repository = repository;
this.byTransaction = false;
this.callDeep = callDeep;
}
public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] balance,
@ -221,6 +224,11 @@ public class ProgramInvokeImpl implements ProgramInvoke {
return byTestingSuite;
}
@Override
public int getCallDeep() {
return this.callDeep;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -251,25 +259,27 @@ public class ProgramInvokeImpl implements ProgramInvoke {
}
@Override
public int hashCode() {
int result = address != null ? address.hashCode() : 0;
result = 31 * result + (origin != null ? origin.hashCode() : 0);
result = 31 * result + (caller != null ? caller.hashCode() : 0);
result = 31 * result + (balance != null ? balance.hashCode() : 0);
result = 31 * result + (gas != null ? gas.hashCode() : 0);
result = 31 * result + (gasPrice != null ? gasPrice.hashCode() : 0);
result = 31 * result + (callValue != null ? callValue.hashCode() : 0);
result = 31 * result + (msgData != null ? Arrays.hashCode(msgData) : 0);
result = 31 * result + (prevHash != null ? prevHash.hashCode() : 0);
result = 31 * result + (coinbase != null ? coinbase.hashCode() : 0);
result = 31 * result + (timestamp != null ? timestamp.hashCode() : 0);
result = 31 * result + (number != null ? number.hashCode() : 0);
result = 31 * result + (difficulty != null ? difficulty.hashCode() : 0);
result = 31 * result + (gaslimit != null ? gaslimit.hashCode() : 0);
result = 31 * result + (storage != null ? storage.hashCode() : 0);
result = 31 * result + (repository != null ? repository.hashCode() : 0);
result = 31 * result + (byTransaction ? 1 : 0);
result = 31 * result + (byTestingSuite ? 1 : 0);
return result;
public String toString() {
return "ProgramInvokeImpl{" +
"address=" + address +
", origin=" + origin +
", caller=" + caller +
", balance=" + balance +
", gas=" + gas +
", gasPrice=" + gasPrice +
", callValue=" + callValue +
", msgData=" + Arrays.toString(msgData) +
", prevHash=" + prevHash +
", coinbase=" + coinbase +
", timestamp=" + timestamp +
", number=" + number +
", difficulty=" + difficulty +
", gaslimit=" + gaslimit +
", storage=" + storage +
", repository=" + repository +
", byTransaction=" + byTransaction +
", byTestingSuite=" + byTestingSuite +
", callDeep=" + callDeep +
'}';
}
}

View File

@ -187,6 +187,8 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
this.repository = repository;
}
@Override
public int getCallDeep() {
return 0;
}
}

View File

@ -4,10 +4,13 @@ import org.ethereum.crypto.HashUtil;
import org.ethereum.vm.Program.OutOfGasException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import static org.ethereum.vm.OpCode.CALL;
import static org.ethereum.vm.OpCode.CREATE;
import static org.ethereum.vm.OpCode.PUSH1;
/**
@ -59,8 +62,6 @@ public class VM {
byte op = program.getCurrentOp();
program.setLastOp(op);
logger.info("[ {} ] Op: [ {} ] Gas: [ {} ]" ,program.getPC(),
OpCode.code(op).name(), program.getGas().longValue());
int oldMemSize = program.getMemSize();
@ -89,6 +90,7 @@ public class VM {
break;
}
String hint = "";
switch (OpCode.code(op)) {
/**
* Stop and Arithmetic Operations
@ -100,13 +102,22 @@ public class VM {
case ADD:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " + " + word2.longValue();
word1.add(word2);
program.stackPush(word1);
program.step();
} break;
case MUL:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " * " + word2.longValue();
word1.mul(word2);
program.stackPush(word1);
program.step();
@ -114,6 +125,10 @@ public class VM {
case SUB:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " - " + word2.longValue();
word1.sub(word2);
program.stackPush(word1);
program.step();
@ -121,6 +136,10 @@ public class VM {
case DIV:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " / " + word2.longValue();
word1.div(word2);
program.stackPush(word1);
program.step();
@ -128,6 +147,10 @@ public class VM {
case SDIV:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.sValue() + " / " + word2.sValue();
word1.sDiv(word2);
program.stackPush(word1);
program.step();
@ -135,6 +158,10 @@ public class VM {
case MOD:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " % " + word2.longValue();
word1.mod(word2);
program.stackPush(word1);
program.step();
@ -142,6 +169,10 @@ public class VM {
case SMOD:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.sValue() + " #% " + word2.sValue();
word1.sMod(word2);
program.stackPush(word1);
program.step();
@ -149,6 +180,10 @@ public class VM {
case EXP:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " ** " + word2.longValue();
word1.exp(word2);
program.stackPush(word1);
program.step();
@ -156,6 +191,10 @@ public class VM {
case NEG:{
DataWord word1 = program.stackPop();
word1.negate();
if (logger.isInfoEnabled())
hint = "" + word1.longValue();
program.stackPush(word1);
program.step();
} break;
@ -163,6 +202,10 @@ public class VM {
// TODO: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " < " + word2.longValue();
if (word1.value().compareTo(word2.value()) == -1) {
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
@ -176,6 +219,10 @@ public class VM {
// TODO: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.sValue() + " < " + word2.sValue();
if (word1.sValue().compareTo(word2.sValue()) == -1) {
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
@ -189,6 +236,10 @@ public class VM {
// TODO: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.sValue() + " > " + word2.sValue();
if (word1.sValue().compareTo(word2.sValue()) == 1) {
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
@ -202,6 +253,10 @@ public class VM {
// TODO: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " < " + word2.longValue();
if (word1.value().compareTo(word2.value()) == 1) {
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
@ -214,6 +269,10 @@ public class VM {
case EQ:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " = " + word2.longValue();
if (word1.xor(word2).isZero()) {
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
@ -230,6 +289,10 @@ public class VM {
} else {
word1.and(DataWord.ZERO);
}
if (logger.isInfoEnabled())
hint = "" + word1.longValue();
program.stackPush(word1);
program.step();
} break;
@ -240,6 +303,10 @@ public class VM {
case AND:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " && " + word2.longValue();
word1.and(word2);
program.stackPush(word1);
program.step();
@ -247,6 +314,10 @@ public class VM {
case OR: {
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " || " + word2.longValue();
word1.or(word2);
program.stackPush(word1);
program.step();
@ -254,6 +325,10 @@ public class VM {
case XOR: {
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " ^ " + word2.longValue();
word1.xor(word2);
program.stackPush(word1);
program.step();
@ -270,6 +345,10 @@ public class VM {
} else {
result = new DataWord();
}
if (logger.isInfoEnabled())
hint = "" + result.longValue();
program.stackPush(result);
program.step();
} break;
@ -285,6 +364,9 @@ public class VM {
byte[] encoded = HashUtil.sha3(buffer.array());
DataWord word = new DataWord(encoded);
if (logger.isInfoEnabled())
hint = word.toString();
program.stackPush(word);
program.step();
} break;
@ -294,27 +376,47 @@ public class VM {
*/
case ADDRESS:{
DataWord address = program.getOwnerAddress();
if (logger.isInfoEnabled())
hint = address.toString();
program.stackPush(address);
program.step();
} break;
case BALANCE:{
DataWord address = program.stackPop();
DataWord balance = program.getBalance(address);
if (logger.isInfoEnabled())
hint = "address: " + address.toString() + " balance: " + balance.longValue();
program.stackPush(balance);
program.step();
} break;
case ORIGIN:{
DataWord originAddress = program.getOriginAddress();
if (logger.isInfoEnabled())
hint = "address: " + originAddress.toString();
program.stackPush(originAddress);
program.step();
} break;
case CALLER:{
DataWord callerAddress = program.getCallerAddress();
if (logger.isInfoEnabled())
hint = "address: " + callerAddress.toString();
program.stackPush(callerAddress);
program.step();
} break;
case CALLVALUE:{
DataWord callValue = program.getCallValue();
if (logger.isInfoEnabled())
hint = "value: " + callValue.toString();
program.stackPush(callValue);
program.step();
} break;
@ -322,11 +424,18 @@ public class VM {
DataWord dataOffs = program.stackPop();
DataWord value = program.getDataValue(dataOffs);
if (logger.isInfoEnabled())
hint = "data: " + value.toString();
program.stackPush(value);
program.step();
} break;
case CALLDATASIZE:{
DataWord dataSize = program.getDataSize();
if (logger.isInfoEnabled())
hint = "size: " + dataSize.longValue();
program.stackPush(dataSize);
program.step();
} break;
@ -336,11 +445,19 @@ public class VM {
DataWord lengthData = program.stackPop();
byte[] msgData = program.getDataCopy(dataOffsetData, lengthData);
if (logger.isInfoEnabled())
hint = "data: " + Hex.toHexString(msgData);
program.memorySave(memOffsetData.data, msgData);
program.step();
} break;
case CODESIZE:{
DataWord length = new DataWord(program.ops.length);
if (logger.isInfoEnabled())
hint = "size: " + length.longValue();
program.stackPush(length);
program.step();
} break;
@ -360,11 +477,18 @@ public class VM {
byte[] code = new byte[length];
System.arraycopy(program.ops, codeOffset, code, 0, length);
if (logger.isInfoEnabled())
hint = "code: " + Hex.toHexString(code);
program.memorySave(memOffsetData.getData(), code);
program.step();
} break;
case GASPRICE:{
DataWord gasPrice = program.getGasPrice();
if (logger.isInfoEnabled())
hint = "price: " + gasPrice.toString();
program.stackPush(gasPrice);
program.step();
} break;
@ -374,31 +498,55 @@ public class VM {
*/
case PREVHASH: {
DataWord prevHash = program.getPrevHash();
if (logger.isInfoEnabled())
hint = "prevHash: " + prevHash;
program.stackPush(prevHash);
program.step();
} break;
case COINBASE: {
DataWord coinbase = program.getCoinbase();
if (logger.isInfoEnabled())
hint = "coinbase: " + coinbase;
program.stackPush(coinbase);
program.step();
} break;
case TIMESTAMP:{
DataWord timestamp = program.getTimestamp();
if (logger.isInfoEnabled())
hint = "timestamp: " + timestamp.longValue();
program.stackPush(timestamp);
program.step();
} break;
case NUMBER:{
DataWord number = program.getNumber();
if (logger.isInfoEnabled())
hint = "number: " + number.longValue();
program.stackPush(number);
program.step();
} break;
case DIFFICULTY:{
DataWord difficulty = program.getDifficulty();
if (logger.isInfoEnabled())
hint = "difficulty: " + difficulty;
program.stackPush(difficulty);
program.step();
} break;
case GASLIMIT:{
DataWord gaslimit = program.getGaslimit();
if (logger.isInfoEnabled())
hint = "gaslimit: " + gaslimit;
program.stackPush(gaslimit);
program.step();
} break;
@ -423,12 +571,20 @@ public class VM {
case MLOAD:{
DataWord addr = program.stackPop();
DataWord data = program.memoryLoad(addr);
if (logger.isInfoEnabled())
hint = "data: " + data;
program.stackPush(data);
program.step();
} break;
case MSTORE:{
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
if (logger.isInfoEnabled())
hint = "addr: " + addr + " value: " + value;
program.memorySave(addr, value);
program.step();
} break;
@ -442,6 +598,10 @@ public class VM {
case SLOAD:{
DataWord key = program.stackPop();
DataWord val = program.storageLoad(key);
if (logger.isInfoEnabled())
hint = "key: " + key + " value: " + val;
if (val == null) {
val = key.and(DataWord.ZERO);
}
@ -452,6 +612,9 @@ public class VM {
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
if (logger.isInfoEnabled())
hint = "addr: " + addr + " value: " + value;
// for gas calculations [YP 9.2]
DataWord oldValue = program.storageLoad(addr);
program.storageSave(addr, value);
@ -465,6 +628,10 @@ public class VM {
} break;
case JUMP:{
DataWord pos = program.stackPop();
if (logger.isInfoEnabled())
hint = "~> " + pos.longValue();
program.setPC(pos);
} break;
case JUMPI:{
@ -476,21 +643,37 @@ public class VM {
} else{
program.step();
}
if (logger.isInfoEnabled())
hint = "~> " + program.getPC();
} break;
case PC:{
int pc = program.getPC();
DataWord pcWord = new DataWord(pc);
if (logger.isInfoEnabled())
hint = pcWord.toString();
program.stackPush(pcWord);
program.step();
} break;
case MSIZE:{
int memSize = program.getMemSize();
DataWord wordMemSize = new DataWord(memSize);
if (logger.isInfoEnabled())
hint = "" + memSize;
program.stackPush(wordMemSize);
program.step();
} break;
case GAS:{
DataWord gas = program.getGas();
if (logger.isInfoEnabled())
hint = "" + gas;
program.stackPush(gas);
program.step();
} break;
@ -503,6 +686,8 @@ public class VM {
int nPush = op - PUSH1.val() + 1;
byte[] data = program.sweep(nPush);
hint = "" + Hex.toHexString(data);
program.stackPush(data);
} break;
case CREATE:{
@ -510,6 +695,10 @@ public class VM {
DataWord inOffset = program.stackPop();
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);
program.createContract(value, inOffset, inSize);
program.step();
@ -525,6 +714,11 @@ public class VM {
DataWord outDataOffs = program.stackPop();
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);
program.callToAddress(gas, toAddress, value, inDataOffs, inDataSize,outDataOffs, outDataSize);
program.step();
@ -536,6 +730,9 @@ public class VM {
ByteBuffer hReturn = program.memoryChunk(offset, size);
program.setHReturn(hReturn);
if (logger.isInfoEnabled())
hint = "data: " + Hex.toHexString(hReturn.array());
program.step();
program.stop();
} break;
@ -548,6 +745,13 @@ public class VM {
}
}
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);
// memory gas calc
int newMemSize = program.getMemSize();
int memoryUsage = (newMemSize - oldMemSize) /32;
@ -576,6 +780,7 @@ public class VM {
program.spendGas(GasCost.TRANSACTION, "TRANSACTION");
program.spendGas(GasCost.TXDATA * program.invokeData.getDataSize().intValue(), "DATA");
}
while(!program.isStopped())
this.step(program);
} catch (RuntimeException e) {

View File

@ -0,0 +1,31 @@
# Root logger option
log4j.rootLogger=DEBUG, stdout, file
# 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.file=org.apache.log4j.rolling.RollingFileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern= %d{HH:mm:ss} [%c{1}] %m%n
log4j.appender.file.RollingPolicy=org.apache.log4j.rolling.TimeBasedRollingPolicy
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.org.ethereum.net = ERROR
log4j.logger.org.ethereum.db = ERROR
log4j.logger.peerdiscovery = ERROR
log4j.logger.java.nio = ERROR
log4j.logger.io.netty = ERROR
log4j.logger.wire = ERROR
log4j.logger.VM = DEBUG
log4j.logger.main = INFO
log4j.logger.state = ERROR
log4j.logger.repository = DEBUG
log4j.logger.blockchain = DEBUG
log4j.logger.ui = ERROR
log4j.logger.gas = ERROR

View File

@ -772,8 +772,15 @@ public class RLPTest {
byte[] output = RLP.encodeList(rlpKeysList, rlpValuesList, rlpCode);
assertEquals(expectedOutput, Hex.toHexString(output));
}
}
@Test
public void encodeBigIntegerEdge_1(){
BigInteger integer = new BigInteger("80", 10);
byte[] encodedData = RLP.encodeBigInteger(integer);
System.out.println(Hex.toHexString(encodedData));
}
}