Merge pull request #79 from nicksavers/master

Better debugging options and other minor fixes
This commit is contained in:
romanman 2014-08-17 00:47:28 +03:00
commit 49604d0eb1
22 changed files with 363 additions and 239 deletions

View File

@ -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 Integer DEFAULT_VMTRACE_BLOCK = 0;
private static String DEFAULT_DATABASE_DIR = System.getProperty("user.dir");
private static Boolean DEFAULT_DUMP_CLEAN_ON_RESTART = true;
private static Boolean DEFAULT_PLAY_VM = true;
@ -151,6 +152,11 @@ public class SystemProperties {
return prop.getProperty("dump.dir");
}
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");

View File

@ -15,8 +15,6 @@ import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@ -45,7 +43,7 @@ public class Block {
private BlockHeader header;
/* Transactions */
private List<TransactionReceipt> txReceiptList = new CopyOnWriteArrayList <TransactionReceipt>() ;
private List<TransactionReceipt> txReceiptList = new CopyOnWriteArrayList<TransactionReceipt>() ;
private List<Transaction> transactionsList = new CopyOnWriteArrayList<Transaction>();
/* Uncles */
@ -73,14 +71,14 @@ public class Block {
this.header = new BlockHeader(parentHash, unclesHash, coinbase,
difficulty, number, minGasPrice, gasLimit, gasUsed,
timestamp, extraData, nonce);
this.transactionsList = transactionsList;
if (this.transactionsList == null){
this.transactionsList = new CopyOnWriteArrayList<Transaction>();
}
this.uncleList = uncleList;
if (this.uncleList == null){
if (this.uncleList == null) {
this.uncleList = new CopyOnWriteArrayList<BlockHeader>();
}
@ -268,7 +266,6 @@ public class Block {
toStringBuff.append(header.toStylishString());
for (TransactionReceipt tx : getTxReceiptList()) {
toStringBuff.append("<br/>");
toStringBuff.append( tx.toStylishString() );
toStringBuff.append("<br/>");

View File

@ -137,11 +137,7 @@ public class Blockchain {
this.processBlock(block);
// Remove all wallet transactions as they already approved by the net
for (Transaction tx : block.getTransactionsList()) {
if (logger.isDebugEnabled())
logger.debug("pending cleanup: tx.hash: [{}]", Hex.toHexString( tx.getHash()));
WorldManager.getInstance().removeWalletTransaction(tx);
}
WorldManager.getInstance().getWallet().removeTransactions(block.getTransactionsList());
EthereumListener listener = WorldManager.getInstance().getListener();
if (listener != null)
@ -150,35 +146,15 @@ public class Blockchain {
EthereumListener ethereumListener = WorldManager.getInstance().getListener();
if (ethereumListener != null)
ethereumListener.onBlock(block);
/*
if (lastBlock.getNumber() >= 30) {
System.out.println("** checkpoint **");
this.close();
WorldManager.getInstance().getRepository().close();
System.exit(1);
}
*/
}
public void processBlock(Block block) {
if(block.isValid()) {
if (!block.isGenesis()) {
if (!CONFIG.blockChainOnly()) {
for (Transaction tx : block.getTransactionsList())
// TODO: refactor the wallet pending transactions to the world manager
WorldManager.getInstance().addWalletTransaction(tx);
this.applyBlock(block);
WorldManager.getInstance().getWallet().addTransactions(block.getTransactionsList());
this.applyBlock(block);
WorldManager.getInstance().getWallet().processBlock(block);
repository.dumpState(block.getNumber(), 0,
null);
}
}
this.storeBlock(block);
@ -190,11 +166,15 @@ public class Blockchain {
public void applyBlock(Block block) {
int i = 0;
for (Transaction tx : block.getTransactionsList()) {
long totalGasUsed = 0;
for (TransactionReceipt txr : block.getTxReceiptList()) {
stateLogger.debug("apply block: [ {} ] tx: [ {} ] ", block.getNumber(), i);
applyTransaction(block, tx, block.getCoinbase());
repository.dumpState(block.getNumber(), i, tx.getHash());
++i;
totalGasUsed += applyTransaction(block, txr.getTransaction());
if(!Arrays.equals(this.repository.getWorldState().getRootHash(), txr.getPostTxState()))
logger.warn("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());
}
// miner reward
@ -205,6 +185,9 @@ public class Blockchain {
for (BlockHeader uncle : block.getUncleList()) {
repository.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
}
if(block.getNumber() >= CONFIG.traceStartBlock())
repository.dumpState(block, totalGasUsed, 0, null);
}
public void storeBlock(Block block) {
@ -216,8 +199,8 @@ public class Blockchain {
if(!blockStateRootHash.equals(worldStateRootHash)){
logger.error("ERROR: STATE CONFLICT! block: {} worldstate {} mismatch", block.getNumber(), worldStateRootHash);
// Last conflict on block 1501 -> worldstate 27920c6c7acd42c8a7ac8a835d4c0e0a45590deb094d6b72a8493fac5d7a3654
// repository.close();
// System.exit(-1); // Don't add block
repository.close();
System.exit(-1); // Don't add block
}
}
@ -230,8 +213,17 @@ public class Blockchain {
logger.info("*** Last block added [ #{} ]", block.getNumber());
}
public void applyTransaction(Block block, Transaction tx, byte[] coinbase) {
/**
* Apply the transaction to the world state
*
* @param block - the block which contains the transactions
* @param tx - the transaction to be applied
* @return gasUsed
*/
public long applyTransaction(Block block, Transaction tx) {
byte[] coinbase = block.getCoinbase();
byte[] senderAddress = tx.getSender();
AccountState senderAccount = repository.getAccountState(senderAddress);
@ -239,7 +231,7 @@ public class Blockchain {
if (stateLogger.isWarnEnabled())
stateLogger.warn("No such address: {}",
Hex.toHexString(senderAddress));
return;
return 0;
}
// 1. VALIDATE THE NONCE
@ -249,7 +241,7 @@ public class Blockchain {
if (stateLogger.isWarnEnabled())
stateLogger.warn("Invalid nonce account.nonce={} tx.nonce={}",
nonce, txNonce);
return;
return 0;
}
// 3. FIND OUT THE TRANSACTION TYPE
@ -300,7 +292,7 @@ public class Blockchain {
if (balance.compareTo(gasDebit) == -1) {
logger.debug("No gas to start the execution: sender={}",
Hex.toHexString(senderAddress));
return;
return 0;
}
repository.addBalance(senderAddress, gasDebit.negate());
senderAccount.subFromBalance(gasDebit); // balance will be read again below
@ -316,23 +308,18 @@ public class Blockchain {
Hex.toHexString(senderAddress), gasDebit);
}
// 3. START TRACKING FOR REVERT CHANGES OPTION !!!
Repository trackRepository = repository.getTrack();
trackRepository.startTracking();
try {
// 4. THE SIMPLE VALUE/BALANCE CHANGE
if (tx.getValue() != null) {
BigInteger senderBalance = senderAccount.getBalance();
if (senderBalance.compareTo(new BigInteger(1, tx.getValue())) >= 0) {
repository.addBalance(receiverAddress,
new BigInteger(1, tx.getValue()));
repository.addBalance(senderAddress,
new BigInteger(1, tx.getValue()).negate());
if (stateLogger.isDebugEnabled())
stateLogger.debug("Update value balance \n "
+ "sender={}, receiver={}, value={}",
@ -342,7 +329,14 @@ public class Blockchain {
}
}
// 5. CREATE OR EXECUTE PROGRAM
// 3. START TRACKING FOR REVERT CHANGES OPTION !!!
Repository trackRepository = repository.getTrack();
trackRepository.startTracking();
long gasUsed = 0;
try {
// 5. CREATE OR EXECUTE PROGRAM
if (isContractCreation || code != null) {
Block currBlock = (block == null) ? this.getLastBlock() : block;
@ -357,13 +351,13 @@ public class Blockchain {
ProgramResult result = program.getResult();
applyProgramResult(result, gasDebit, trackRepository,
senderAddress, receiverAddress, coinbase, isContractCreation);
gasUsed = result.getGasUsed();
} else {
// refund everything except fee (500 + 5*txdata)
BigInteger gasPrice = new BigInteger(1, tx.getGasPrice());
long dataFee = tx.getData() == null ? 0: tx.getData().length * GasCost.TXDATA;
long minTxFee = GasCost.TRANSACTION + dataFee;
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(
minTxFee).multiply(gasPrice));
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(minTxFee).multiply(gasPrice));
if (refund.signum() > 0) {
// gas refund
repository.addBalance(senderAddress, refund);
@ -372,10 +366,11 @@ public class Blockchain {
}
} catch (RuntimeException e) {
trackRepository.rollback();
return;
return 0;
}
trackRepository.commit();
pendingTransactions.put(Hex.toHexString(tx.getHash()), tx);
return gasUsed;
}
/**

View File

@ -23,6 +23,14 @@ public class TransactionReceipt {
this.postTxState = postTxState;
this.cumulativeGas = cumulativeGas;
}
public Transaction getTransaction() {
return transaction;
}
public byte[] getPostTxState() {
return postTxState;
}
public byte[] getEncoded() {

View File

@ -1,6 +1,10 @@
package org.ethereum.core;
import org.ethereum.crypto.ECKey;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.net.submit.WalletTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
@ -22,6 +26,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* The Wallet handles the management of accounts with addresses and private keys.
@ -29,17 +34,23 @@ import java.util.Map;
*/
public class Wallet {
private Logger logger = LoggerFactory.getLogger("wallet");
// TODO: a) the values I need to keep for address state is balance & nonce & ECKey
// TODO: b) keep it to be easy accessed by the toAddress()
// private HashMap<Address, BigInteger> rows = new HashMap<>();
// This map of transaction designed
// to approve the tx by external trusted peer
private Map<String, WalletTransaction> walletTransactions = new ConcurrentHashMap<>();
// <address, info> table for a wallet
private Map<String, Account> rows = new HashMap<>();
private long high;
private List<WalletListener> listeners = new ArrayList<>();
private Map<BigInteger, Transaction> transactionMap = new HashMap<>();
private Map<ByteArrayWrapper, Transaction> transactionMap = new HashMap<>();
public void addNewAccount() {
Account account = new Account();
@ -80,10 +91,51 @@ public class Wallet {
}
return sum;
}
/***********************************************************************
* 1) the dialog put a pending transaction on the list
* 2) the dialog send the transaction to a net
* 3) wherever the transaction got in from the wire it will change to approve state
* 4) only after the approve a) Wallet state changes
* 5) After the block is received with that tx the pending been clean up
*/
public WalletTransaction addTransaction(Transaction transaction) {
String hash = Hex.toHexString(transaction.getHash());
logger.info("pending transaction placed hash: {}", hash );
WalletTransaction walletTransaction = this.walletTransactions.get(hash);
if (walletTransaction != null)
walletTransaction.incApproved();
else {
walletTransaction = new WalletTransaction(transaction);
this.walletTransactions.put(hash, walletTransaction);
}
return walletTransaction;
}
public void addTransactions(List<Transaction> transactions) {
for (Transaction transaction : transactions) {
this.addTransaction(transaction);
}
}
public void removeTransactions(List<Transaction> transactions) {
for (Transaction tx : transactions) {
if (logger.isDebugEnabled())
logger.debug("pending cleanup: tx.hash: [{}]", Hex.toHexString(tx.getHash()));
this.removeTransaction(tx);
}
}
public void removeTransaction(Transaction transaction) {
String hash = Hex.toHexString(transaction.getHash());
logger.info("pending transaction removed with hash: {} ", hash);
walletTransactions.remove(hash);
}
public void applyTransaction(Transaction transaction) {
transactionMap.put(new BigInteger(transaction.getHash()), transaction );
transactionMap.put(new ByteArrayWrapper(transaction.getHash()), transaction );
byte[] senderAddress = transaction.getSender();
Account sender = rows.get(Hex.toHexString(senderAddress));
@ -113,7 +165,7 @@ public class Wallet {
List<Transaction> transactions = block.getTransactionsList();
for (Transaction tx : transactions) {
boolean txExist = transactionMap.get(new BigInteger(tx.getHash())) != null;
boolean txExist = transactionMap.get(new ByteArrayWrapper(tx.getHash())) != null;
if (txExist) break;
else {
this.applyTransaction(tx);

View File

@ -70,13 +70,7 @@ public class ContractDetails {
if (storageKeys.size() == 0)
return null;
int foundIndex = -1;
for (int i = 0; i < storageKeys.size(); ++i) {
if (storageKeys.get(i).equals(key)) {
foundIndex = i;
break;
}
}
int foundIndex = storageKeys.indexOf(key);
if (foundIndex != -1)
return storageValues.get(foundIndex);
else
@ -97,7 +91,8 @@ public class ContractDetails {
storageTrie = new Trie(null);
// calc the trie for root hash
for (int i = 0; i < storageKeys.size(); ++i){
storageTrie.update(storageKeys.get(i).getData(), RLP.encodeElement( storageValues.get(i).getNoLeadZeroesData() ));
storageTrie.update(storageKeys.get(i).getData(), RLP
.encodeElement(storageValues.get(i).getNoLeadZeroesData()));
}
return storageTrie.getRootHash();
}

View File

@ -16,16 +16,17 @@ import org.ethereum.vm.DataWord;
import org.iq80.leveldb.DBIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import static org.ethereum.config.SystemProperties.CONFIG;
@ -136,7 +137,7 @@ public class Repository {
}
logger.debug("Block #{} -> {}", Genesis.NUMBER, blockchain.getLastBlock().toFlatString());
dumpState(0, 0, null);
dumpState(Genesis.getInstance(), 0, 0, null);
} else {
logger.debug("Displaying blocks stored in DB sorted on blocknumber");
@ -368,13 +369,14 @@ public class Repository {
return stateDB.dumpKeys();
}
public void dumpState(long blockNumber, int txNumber, byte[] txHash) {
public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash) {
if (!CONFIG.dumpFull()) return;
if (!(CONFIG.dumpFull() || CONFIG.dumpBlock() == block.getNumber()))
return;
// todo: dump block header and the relevant tx
if (blockNumber == 0 && txNumber == 0)
if (block.getNumber() == 0 && txNumber == 0)
if (CONFIG.dumpCleanOnRestart()) {
try {FileUtils.deleteDirectory(CONFIG.dumpDir());} catch (IOException e) {}
}
@ -383,10 +385,10 @@ public class Repository {
String fileName = "";
if (txHash != null)
fileName = String.format("%d_%d_%s.dmp",
blockNumber, txNumber, Hex.toHexString(txHash).substring(0, 8));
fileName = String.format("%d_%d_%s.dmp", block.getNumber(), txNumber,
Hex.toHexString(txHash).substring(0, 8));
else
fileName = String.format("%d_c.dmp", blockNumber);
fileName = String.format("%d_c.dmp", block.getNumber());
File dumpFile = new File(System.getProperty("user.dir") + "/" + dir + fileName);
FileWriter fw = null;
@ -400,35 +402,16 @@ public class Repository {
bw = new BufferedWriter(fw);
List<ByteArrayWrapper> keys = this.detailsDB.dumpKeys();
// dump json file
for (ByteArrayWrapper key : keys) {
byte[] keyBytes = key.getData();
AccountState state = getAccountState(keyBytes);
ContractDetails details = getContractDetails(keyBytes);
BigInteger nonce = (state != null)? state.getNonce():null;
BigInteger balance = (state != null)? state.getBalance():null;
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 != null)? BigIntegers.asUnsignedByteArray(nonce) : null,
(nonce != null)? BigIntegers.asUnsignedByteArray(balance): null,
stateRoot, codeHash, code, storage);
bw.write(accountLine);
bw.write("\n");
}
JsonNodeFactory jsonFactory = new JsonNodeFactory(false);
ObjectNode blockNode = jsonFactory.objectNode();
JSONHelper.dumpBlock(blockNode, block, gasUsed, this.getWorldState().getRootHash(), keys, this);
bw.write(blockNode.toString());
bw.write("\n");
String rootHash = Hex.toHexString(this.getWorldState().getRootHash());
bw.write(
String.format(" => Global State Root: [ %s ]", rootHash)
bw.write(String.format(" => Global State Root: [ %s ]", rootHash)
);
} catch (IOException e) {

View File

@ -5,8 +5,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.ethereum.util.LRUMap;
/**
* www.ethereumJ.com
*

View File

@ -1,15 +1,41 @@
package org.ethereum.json;
import org.ethereum.util.RLP;
import org.ethereum.core.AccountState;
import org.ethereum.core.Block;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.db.ContractDetails;
import org.ethereum.db.Repository;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.DataWord;
import org.json.simple.JSONArray;
import org.json.simple.JSONValue;
import org.spongycastle.util.encoders.Hex;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.math.BigInteger;
import java.util.*;
/**
* JSON Helper class to format data into ObjectNodes
* to match PyEthereum blockstate output
*
* Dump format:
* {
* "address":
* {
* "nonce": "n1",
* "balance": "b1",
* "stateRoot": "s1",
* "codeHash": "c1",
* "code": "c2",
* "storage":
* {
* "key1": "value1",
* "key2": "value2"
* }
* }
* }
*
* www.ethereumJ.com
*
* @author: Roman Mandeleil
@ -17,46 +43,62 @@ import java.util.*;
*/
public class JSONHelper {
public static String dumpLine(byte[] address, byte[] nonce, byte[] balance, byte[] stateRoot,
byte[] codeHash, byte[] code, Map<DataWord, DataWord> storageMap) {
public static void dumpState(ObjectNode statesNode, String address, AccountState state, ContractDetails details) {
// {address: x, nonce: n1, balance: b1, stateRoot: s1, codeHash: c1, code: c2, sotrage: [key: k1, value: v1, key:k2, value: v2 ] }
List<DataWord> storageKeys = new ArrayList<DataWord>(storageMap.keySet());
List<DataWord> storageKeys = new ArrayList<>(details.getStorage().keySet());
Collections.sort((List<DataWord>) storageKeys);
Map<String, String> outMap = new LinkedHashMap<String, String>();
ObjectNode account = statesNode.objectNode();
ObjectNode storage = statesNode.objectNode();
for (DataWord key : storageKeys) {
outMap.put(Hex.toHexString(key.getData()),
Hex.toHexString(
RLP.encodeElement( storageMap.get(key).getNoLeadZeroesData() )
));
storage.put("0x" + Hex.toHexString(key.getData()),
"0x" + Hex.toHexString(details.getStorage().get(key).getNoLeadZeroesData()));
}
String mapString = JSONValue.toJSONString(outMap);
mapString = mapString.replace("\"", "");
JSONArray orderFields = new JSONArray();
orderFields.add("address: " + Hex.toHexString(address));
orderFields.add(" nonce: " + (nonce == null ? "00" : new BigInteger(1, nonce).toString()));
orderFields.add(" balance: " + (balance == null ? "00" : new BigInteger(1, 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)));
orderFields.add(" storage: " + mapString);
String out = orderFields.toString();
out = out.replace("\"", "");
return out;
account.put("balance", state.getBalance() == null ? "0" : state.getBalance().toString());
// account.put("codeHash", details.getCodeHash() == null ? "0x" : "0x" + Hex.toHexString(details.getCodeHash()));
account.put("code", details.getCode() == null ? "0x" : "0x" + Hex.toHexString(details.getCode()));
account.put("nonce", state.getNonce() == null ? "0" : state.getNonce().toString());
account.put("storage", storage);
account.put("storage_root", state.getStateRoot() == null ? "" : Hex.toHexString(state.getStateRoot()));
statesNode.put(address, account);
}
public static void dumpBlock(ObjectNode blockNode, Block block,
long gasUsed, byte[] state, List<ByteArrayWrapper> keys,
Repository repository) {
blockNode.put("coinbase", Hex.toHexString(block.getCoinbase()));
blockNode.put("difficulty", new BigInteger(1, block.calcDifficulty()).toString());
blockNode.put("extra_data", "0x");
blockNode.put("gas_limit", String.valueOf(block.calcGasLimit()));
blockNode.put("gas_used", String.valueOf(gasUsed));
blockNode.put("min_gas_price", String.valueOf(block.getMinGasPrice()));
blockNode.put("nonce", "0x" + Hex.toHexString(block.getNonce()));
blockNode.put("number", String.valueOf(block.getNumber()));
blockNode.put("prevhash", "0x" + Hex.toHexString(block.getParentHash()));
ObjectNode statesNode = blockNode.objectNode();
for (ByteArrayWrapper key : keys) {
byte[] keyBytes = key.getData();
AccountState accountState = repository.getAccountState(keyBytes);
ContractDetails details = repository.getContractDetails(keyBytes);
JSONHelper.dumpState(statesNode, Hex.toHexString(keyBytes), accountState, details);
}
blockNode.put("state", statesNode);
blockNode.put("state_root", Hex.toHexString(state));
blockNode.put("timestamp", String.valueOf(block.getTimestamp()));
ArrayNode transactionsNode = blockNode.arrayNode();
blockNode.put("transactions", transactionsNode);
blockNode.put("tx_list_root", ByteUtil.toHexString(block.getTxTrieRoot()));
blockNode.put("uncles_hash", "0x" + Hex.toHexString(block.getUnclesHash()));
// JSONHelper.dumpTransactions(blockNode,
// stateRoot, codeHash, code, storage);
}
}
}

View File

@ -10,7 +10,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.ethereum.core.AccountState;
import org.ethereum.core.Blockchain;
import org.ethereum.core.Transaction;
import org.ethereum.core.Wallet;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
@ -19,10 +18,6 @@ import org.ethereum.listener.EthereumListener;
import org.ethereum.net.client.ClientPeer;
import org.ethereum.net.client.PeerData;
import org.ethereum.net.peerdiscovery.PeerDiscovery;
import org.ethereum.net.submit.WalletTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
/**
* WorldManager is the main class to handle the processing of transactions and
@ -34,8 +29,6 @@ import org.spongycastle.util.encoders.Hex;
*/
public class WorldManager {
private Logger logger = LoggerFactory.getLogger("main");
private Blockchain blockchain;
private Repository repository;
private Wallet wallet;
@ -44,11 +37,6 @@ public class WorldManager {
private List<PeerData> peers = new CopyOnWriteArrayList<PeerData>();
private ClientPeer activePeer;
// This map of transaction designed
// to approve the tx by external trusted peer
private Map<String, WalletTransaction> walletTransactions =
Collections.synchronizedMap(new HashMap<String, WalletTransaction>());
private EthereumListener listener;
private static WorldManager instance;
@ -83,34 +71,7 @@ public class WorldManager {
}
return instance;
}
/***********************************************************************
* 1) the dialog put a pending transaction on the list
* 2) the dialog send the transaction to a net
* 3) wherever the transaction got in from the wire it will change to approve state
* 4) only after the approve a) Wallet state changes
* 5) After the block is received with that tx the pending been clean up
*/
public WalletTransaction addWalletTransaction(Transaction transaction) {
String hash = Hex.toHexString(transaction.getHash());
logger.info("pending transaction placed hash: {}", hash );
WalletTransaction walletTransaction = this.walletTransactions.get(hash);
if (walletTransaction != null)
walletTransaction.incApproved();
else {
walletTransaction = new WalletTransaction(transaction);
this.walletTransactions.put(hash, walletTransaction);
}
return walletTransaction;
}
public void removeWalletTransaction(Transaction transaction) {
String hash = Hex.toHexString(transaction.getHash());
logger.info("pending transaction removed with hash: {} ", hash);
walletTransactions.remove(hash);
}
public void setRepository(Repository repository) {
this.repository = repository;
}

View File

@ -199,7 +199,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
List<Transaction> txList = transactionsMessage.getTransactions();
for(Transaction tx : txList)
WorldManager.getInstance().getBlockchain()
.applyTransaction(null, tx, null);
.applyTransaction(null, tx);
logger.info(transactionsMessage.toString());
if (peerListener != null) peerListener.console(transactionsMessage.toString());

View File

@ -36,11 +36,14 @@ public class StaticMessages {
String version = SystemProperties.CONFIG.projectVersion();
String system = System.getProperty("os.name");
if (System.getProperty("java.vm.vendor").contains("Android")) system = "Android";
if (system.contains(" "))
system = system.substring(0, system.indexOf(" "));
if (System.getProperty("java.vm.vendor").contains("Android"))
system = "Android";
String phrase = SystemProperties.CONFIG.helloPhrase();
String helloAnnouncement = String.format("Ethereum(J)/v%s/Release/%s/%s ", version, system, phrase);
String helloAnnouncement = String.format("EthereumJ/v%s/%s/%s/Java", version, phrase, system);
return new HelloMessage((byte) 0x17, (byte) 0x00,
helloAnnouncement, Byte.parseByte("00000111", 2),

View File

@ -17,7 +17,7 @@ import static java.lang.Thread.sleep;
*/
public class TransactionTask implements Callable<Transaction> {
private Logger logger = LoggerFactory.getLogger("TransactionTask");
private Logger logger = LoggerFactory.getLogger(TransactionTask.class);
private Transaction tx;
@ -33,17 +33,16 @@ public class TransactionTask implements Callable<Transaction> {
ClientPeer peer = WorldManager.getInstance().getActivePeer();
WalletTransaction walletTransaction = WorldManager.getInstance()
.addWalletTransaction(tx);
WalletTransaction walletTransaction = WorldManager.getInstance().getWallet().addTransaction(tx);
peer.sendTransaction(tx);
while(walletTransaction.getApproved() < 1 ) {
while(walletTransaction.getApproved() < 1) {
sleep(10);
}
logger.info("return approved: {}", walletTransaction.getApproved());
} catch (Throwable th) {
logger.info("exception caugh: {}", th);
WorldManager.getInstance().removeWalletTransaction(tx);
WorldManager.getInstance().getWallet().removeTransaction(tx);
}
return null;
}

View File

@ -15,12 +15,10 @@ import static org.ethereum.config.SystemProperties.CONFIG;
public class AdvancedDeviceUtils {
public static void adjustDetailedTracing(long blockNum){
public static void adjustDetailedTracing(long blockNum) {
// here we can turn on the detail tracing in the middle of the chain
if (blockNum >= CONFIG.traceStartBlock() && CONFIG.traceStartBlock() != -1) {
URL configFile = ClassLoader
.getSystemResource("log4j-detailed.properties");
URL configFile = ClassLoader.getSystemResource("log4j-detailed.properties");
PropertyConfigurator.configure(configFile);
}
}

View File

@ -20,7 +20,7 @@ import java.util.Stack;
* Created on: 01/06/2014 10:45
*/
public class Program {
private Logger logger = LoggerFactory.getLogger("VM");
private Logger gasLogger = LoggerFactory.getLogger("gas");
private int invokeHash;
@ -302,7 +302,7 @@ public class Program {
trackRepository.commit();
// 5. REFUND THE REMAIN GAS
int refundGas = gas - result.getGasUsed();
long refundGas = gas - result.getGasUsed();
if (refundGas > 0) {
this.refundGas(refundGas, "remain gas from the internal call");
if (logger.isInfoEnabled()){
@ -376,7 +376,6 @@ public class Program {
// actual gas subtract
this.spendGas(gas.intValue(), "internal call");
Repository trackRepository = result.getRepository().getTrack();
trackRepository.startTracking();
trackRepository.addBalance(toAddress, endowmentValue.value());
@ -390,7 +389,6 @@ public class Program {
ProgramResult result = null;
if (programCode != null && programCode.length != 0) {
VM vm = new VM();
Program program = new Program(programCode, programInvoke);
vm.play(program);
@ -451,7 +449,7 @@ public class Program {
result.spendGas(gasValue);
}
public void refundGas(int gasValue, String cause) {
public void refundGas(long gasValue, String cause) {
gasLogger.info("[{}] Refund for cause: [ {} ], gas: [ {} ]", invokeHash, cause, gasValue);
result.refundGas(gasValue);
}
@ -659,6 +657,7 @@ public class Program {
public void output(String out);
}
@SuppressWarnings("serial")
public class OutOfGasException extends RuntimeException {
}
}

View File

@ -13,7 +13,7 @@ import java.util.List;
*/
public class ProgramResult {
private int gasUsed = 0;
private long gasUsed = 0;
private ByteBuffer hReturn = null;
private RuntimeException exception;
private List<DataWord> deleteAccounts;
@ -30,7 +30,7 @@ public class ProgramResult {
public void spendGas(int gas) {
gasUsed += gas;
}
public void refundGas(int gas) {
public void refundGas(long gas) {
gasUsed -= gas;
}
@ -47,7 +47,7 @@ public class ProgramResult {
return exception;
}
public int getGasUsed() {
public long getGasUsed() {
return gasUsed;
}

View File

@ -1,13 +1,18 @@
package org.ethereum.vm;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.ContractDetails;
import org.ethereum.vm.Program.OutOfGasException;
import static org.ethereum.config.SystemProperties.CONFIG;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.ethereum.vm.OpCode.CALL;
import static org.ethereum.vm.OpCode.CREATE;
@ -54,9 +59,10 @@ import static org.ethereum.vm.OpCode.PUSH1;
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: [ {} ]\t Gas: [ {} ]\t Deep: [ {} ] Hint: [ {} ]";
public void step(Program program) {
try {
@ -70,6 +76,34 @@ public class VM {
long gasBefore = program.getGas().longValue();
int stepBefore = program.getPC();
if(program.getNumber().intValue() == CONFIG.dumpBlock()) {
switch (OpCode.code(op)) {
case STOP: case RETURN: case SUICIDE:
ContractDetails details = program.getResult().getRepository()
.getContractDetails(program.getOwnerAddress().getLast20Bytes());
List<DataWord> storageKeys = new ArrayList<>(details.getStorage().keySet());
Collections.sort((List<DataWord>) 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);
}
switch (OpCode.code(op)) {
case SHA3:
program.spendGas(GasCost.SHA3, OpCode.code(op).name());
@ -108,7 +142,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " + " + word2.longValue();
hint = word1.value() + " + " + word2.value();
word1.add(word2);
program.stackPush(word1);
@ -120,7 +154,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " * " + word2.longValue();
hint = word1.value() + " * " + word2.value();
word1.mul(word2);
program.stackPush(word1);
@ -131,7 +165,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " - " + word2.longValue();
hint = word1.value() + " - " + word2.value();
word1.sub(word2);
program.stackPush(word1);
@ -142,7 +176,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " / " + word2.longValue();
hint = word1.value() + " / " + word2.value();
word1.div(word2);
program.stackPush(word1);
@ -164,7 +198,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " % " + word2.longValue();
hint = word1.value() + " % " + word2.value();
word1.mod(word2);
program.stackPush(word1);
@ -186,7 +220,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " ** " + word2.longValue();
hint = word1.value() + " ** " + word2.value();
word1.exp(word2);
program.stackPush(word1);
@ -197,7 +231,7 @@ public class VM {
word1.negate();
if (logger.isInfoEnabled())
hint = "" + word1.longValue();
hint = "" + word1.value();
program.stackPush(word1);
program.step();
@ -208,7 +242,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " < " + word2.longValue();
hint = word1.value() + " < " + word2.value();
if (word1.value().compareTo(word2.value()) == -1) {
word1.and(DataWord.ZERO);
@ -259,7 +293,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " < " + word2.longValue();
hint = word1.value() + " < " + word2.value();
if (word1.value().compareTo(word2.value()) == 1) {
word1.and(DataWord.ZERO);
@ -275,7 +309,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " == " + word2.longValue();
hint = word1.value() + " == " + word2.value();
if (word1.xor(word2).isZero()) {
word1.and(DataWord.ZERO);
@ -295,7 +329,7 @@ public class VM {
}
if (logger.isInfoEnabled())
hint = "" + word1.longValue();
hint = "" + word1.value();
program.stackPush(word1);
program.step();
@ -309,7 +343,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " && " + word2.longValue();
hint = word1.value() + " && " + word2.value();
word1.and(word2);
program.stackPush(word1);
@ -320,7 +354,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " || " + word2.longValue();
hint = word1.value() + " || " + word2.value();
word1.or(word2);
program.stackPush(word1);
@ -331,7 +365,7 @@ public class VM {
DataWord word2 = program.stackPop();
if (logger.isInfoEnabled())
hint = word1.longValue() + " ^ " + word2.longValue();
hint = word1.value() + " ^ " + word2.value();
word1.xor(word2);
program.stackPush(word1);
@ -351,7 +385,7 @@ public class VM {
}
if (logger.isInfoEnabled())
hint = "" + result.longValue();
hint = "" + result.value();
program.stackPush(result);
program.step();
@ -440,7 +474,7 @@ public class VM {
DataWord dataSize = program.getDataSize();
if (logger.isInfoEnabled())
hint = "size: " + dataSize.longValue();
hint = "size: " + dataSize.value();
program.stackPush(dataSize);
program.step();
@ -462,7 +496,7 @@ public class VM {
DataWord length = new DataWord(program.ops.length);
if (logger.isInfoEnabled())
hint = "size: " + length.longValue();
hint = "size: " + length.value();
program.stackPush(length);
program.step();
@ -524,7 +558,7 @@ public class VM {
DataWord timestamp = program.getTimestamp();
if (logger.isInfoEnabled())
hint = "timestamp: " + timestamp.longValue();
hint = "timestamp: " + timestamp.value();
program.stackPush(timestamp);
program.step();
@ -533,7 +567,7 @@ public class VM {
DataWord number = program.getNumber();
if (logger.isInfoEnabled())
hint = "number: " + number.longValue();
hint = "number: " + number.value();
program.stackPush(number);
program.step();
@ -636,7 +670,7 @@ public class VM {
DataWord pos = program.stackPop();
if (logger.isInfoEnabled())
hint = "~> " + pos.longValue();
hint = "~> " + pos.value();
program.setPC(pos);
} break;
@ -703,7 +737,7 @@ public class VM {
if (logger.isInfoEnabled())
logger.info(logString, program.getPC(), OpCode.code(op)
.name(), program.getGas().longValue(),
.name(), program.getGas().value(),
program.invokeData.getCallDeep(), hint);
program.createContract(value, inOffset, inSize);
@ -723,7 +757,7 @@ public class VM {
if (logger.isInfoEnabled())
logger.info(logString, program.getPC(), OpCode.code(op)
.name(), program.getGas().longValue(),
.name(), program.getGas().value(),
program.invokeData.getCallDeep(), hint);
program.callToAddress(gas, toAddress, value, inDataOffs, inDataSize, outDataOffs, outDataSize);
@ -756,8 +790,8 @@ public class VM {
}
}
if (logger.isInfoEnabled())
if (!OpCode.code(op).equals(CALL) && !OpCode.code(op).equals(CREATE))
if (logger.isInfoEnabled() && !OpCode.code(op).equals(CALL)
&& !OpCode.code(op).equals(CREATE))
logger.info(logString, stepBefore, OpCode.code(op).name(),
gasBefore, program.invokeData.getCallDeep(), hint);

View File

@ -21,11 +21,15 @@ log4j.logger.peerdiscovery = ERROR
log4j.logger.java.nio = ERROR
log4j.logger.io.netty = ERROR
log4j.logger.wire = ERROR
log4j.logger.wallet = ERROR
log4j.logger.VM = DEBUG
log4j.logger.dump = OFF
log4j.logger.main = INFO
log4j.logger.trie = ERROR
log4j.logger.state = DEBUG
log4j.logger.repository = DEBUG
log4j.logger.repository = INFO
log4j.logger.blockchain = INFO
log4j.logger.ui = ERROR
log4j.logger.txs = ERROR
log4j.logger.gas = ERROR

View File

@ -15,8 +15,9 @@ log4j.appender.file.RollingPolicy.FileNamePattern=./logs/ethereum_%d{yyyy-MM-dd}
# filter noisy classes
log4j.logger.org.ethereum.core = ERROR
log4j.logger.org.ethereum.net = ERROR
log4j.logger.org.ethereum.db = ERROR
log4j.logger.wallet = ERROR
log4j.logger.net = ERROR
log4j.logger.db = ERROR
log4j.logger.peerdiscovery = ERROR
log4j.logger.java.nio = ERROR
log4j.logger.io.netty = ERROR

View File

@ -25,12 +25,12 @@ peer.discovery.ip.list = 185.43.109.23:30303, \
#peer.active.port = 30303
# RomanJ
peer.active.ip = 54.211.14.10
peer.active.port = 30303
#peer.active.ip = 54.211.14.10
#peer.active.port = 30303
# PoC-5 testnet
#peer.active.ip = 185.43.109.23
#peer.active.port = 30303
peer.active.ip = 185.43.109.23
peer.active.port = 30303
#peer.active.ip = 54.72.69.180
#peer.active.port = 30303
@ -99,6 +99,7 @@ coinbase.secret = monkey
# posible values [true/false]
dump.full = false
dump.dir = dmp
dump.block = 1501
# clean the dump dir each start
dump.clean.on.restart = true
@ -130,7 +131,6 @@ max.blocks.ask = 100
# recommended value: [100.300]
max.blocks.queued = 300
# project version auto copied during build phase
project.version = PROJECT.VERSION

View File

@ -9,17 +9,20 @@ log4j.appender.stdout.layout.ConversionPattern= %d{HH:mm:ss} [%c{1}] %m%n
# filter noisy classes
log4j.logger.org.ethereum.core = ERROR
log4j.logger.org.ethereum.net = ERROR
log4j.logger.org.ethereum.db = ERROR
log4j.logger.wallet = ERROR
log4j.logger.net = ERROR
log4j.logger.db = ERROR
log4j.logger.peerdiscovery = ERROR
log4j.logger.java.nio = ERROR
log4j.logger.io.netty = ERROR
log4j.logger.wire = ERROR
log4j.logger.VM = OFF
log4j.logger.main = INFO
log4j.logger.trie = ERROR
log4j.logger.state = ERROR
log4j.logger.repository = ERROR
log4j.logger.blockchain = DEBUG
log4j.logger.txs = ERROR
log4j.logger.ui = ERROR
log4j.logger.gas = ERROR

View File

@ -74,3 +74,49 @@ database.reset = true
# to be eventually the address
# that get the miner reward
coinbase.secret = monkey
# for testing purposes
# all the state will be dumped
# in JSON form to [dump.dir]
# if [dump.full] = true
# posible values [true/false]
dump.full = false
dump.dir = dmp
dump.block = 1501
# clean the dump dir each start
dump.clean.on.restart = true
# make changes to tracing options
# starting from certain block
# -1 don't make any tracing changes
trace.startblock = -1
# invoke vm program on
# message received,
# if the vm is not invoked
# the balance transfer
# occurs anyway [true/false]
play.vm = true
# maximum blocks to ask,
# when downloading the chain
# sequenteally sending GET_CHAIN msg
# we specify number of block we want
# to get, recomendec value [1..100]
max.blocks.ask = 100
# the network layer will ask for
# more and more blocks independently
# from how much of them been executed
# how much block we will keep in buffer
# until the execution is set by this param
# recommended value: [100.300]
max.blocks.queued = 300
# project version auto copied during build phase
project.version = PROJECT.VERSION
# hello phrase will be included in
# the hello message of the peer
hello.phrase = RJ