VMComplexTest introduced

This commit is contained in:
romanman 2014-06-17 16:35:11 +01:00
parent 82dd0f9761
commit f7a264b0a8
14 changed files with 386 additions and 59 deletions

View File

@ -1,6 +1,5 @@
package org.ethereum.gui;
import com.sun.javafx.binding.StringFormatter;
import org.ethereum.core.Account;
import org.ethereum.core.AccountState;
import org.ethereum.core.ContractDetails;
@ -19,13 +18,10 @@ import org.spongycastle.util.encoders.Hex;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.*;
import java.math.BigInteger;

View File

@ -39,10 +39,10 @@ public class ToolBar extends JFrame {
public ToolBar() throws HeadlessException {
introLogger.info("");
introLogger.info("<> EthereumJ [v0.5.1] by RomanJ");
introLogger.info("<> Code by Roman Mandeleil, (c) 2014.");
introLogger.info("<> Contribution: Nick Savers ");
introLogger.info("<> Based on a design by Vitalik Buterin.");
introLogger.info("|Ξ| EthereumJ [v0.5.1] by RomanJ");
introLogger.info("|Ξ| Code by Roman Mandeleil, (c) 2014.");
introLogger.info("|Ξ| Contribution: Nick Savers ");
introLogger.info("|Ξ| Based on a design by Vitalik Buterin.");
introLogger.info("");
introLogger.info("java.version: " + System.getProperty("java.version"));
introLogger.info("java.home: " + System.getProperty("java.home"));

View File

@ -76,7 +76,7 @@ public class WorldManager {
public void applyTransaction(Transaction tx) {
// TODO: refactor the wallet transactions to the world manager
// TODO: refactor the wallet pending transactions to the world manager
if (blockChain != null)
blockChain.addWalletTransaction(tx);

View File

@ -66,7 +66,8 @@ public class PeerData {
@Override
public String toString() {
return "Peer: [ip=" + getInetAddress().getHostAddress() + ", port=" + getPort() + ", peerId=" + Hex.toHexString(getPeerId()) + "]";
return "Peer: [ip=" + getInetAddress().getHostAddress() + ", port=" + getPort() +
", peerId=" + (getPeerId() == null ? "": Hex.toHexString(getPeerId())) + "]";
}
@Override

View File

@ -21,6 +21,7 @@ import java.util.*;
public class Program {
private Logger logger = LoggerFactory.getLogger("VM");
private Logger gasLogger = null;
ProgramListener listener;
Stack<DataWord> stack = new Stack<DataWord>();
@ -38,6 +39,8 @@ public class Program {
public Program(byte[] ops, ProgramInvoke invokeData) {
gasLogger = LoggerFactory.getLogger("gas - " + invokeData.hashCode());
result.setStateDb(invokeData.getStateDb());
result.setChainDb(invokeData.getChainDb());
result.setDetailDB(invokeData.getDetaildDB());
@ -47,8 +50,14 @@ public class Program {
this.invokeData = invokeData;
this.ops = ops;
spendGas(GasCost.TRANSACTION);
spendGas(GasCost.TXDATA * invokeData.getDataSize().intValue());
// In case the program invoked by wire got
// transaction, this will be the gas cost,
// otherwise the call done by other contract
// charged by CALL op
if (invokeData.byTransaction()){
spendGas(GasCost.TRANSACTION, "TRANSACTION");
spendGas(GasCost.TXDATA * invokeData.getDataSize().intValue(), "DATA");
}
if (invokeData.getStorage() != null){
storage = invokeData.getStorage();
@ -220,13 +229,13 @@ public class Program {
* That method implement internal calls
* and code invocations
*
* @param gas
* @param toAddressDW
* @param endowmentValue
* @param inDataOffs
* @param inDataSize
* @param outDataOffs
* @param outDataSize
* @param gas - gas to pay for the call, remain gas will be refunded to the caller
* @param toAddressDW - address to call
* @param endowmentValue - the value that can be transfer along with the code execution
* @param inDataOffs - start of memory to be input data to the call
* @param inDataSize - size of memory to be input data to the call
* @param outDataOffs - start of memory to be output of the call
* @param outDataSize - size of memory to be output data to the call
*/
public void callToAddress(DataWord gas, DataWord toAddressDW, DataWord endowmentValue,
DataWord inDataOffs, DataWord inDataSize,DataWord outDataOffs, DataWord outDataSize){
@ -252,11 +261,6 @@ public class Program {
receiverState = new AccountState(accountData);
}
// todo: endowment rollbacked move it from here
receiverState.addToBalance(endowmentValue.value());
result.getStateDb().update(toAddress, receiverState.getEncoded());
// todo: endowment rollbacked move it from here
byte[] programCode = result.getChainDb().get(receiverState.getCodeHash());
if (programCode != null && programCode.length != 0){
@ -285,7 +289,6 @@ public class Program {
return;
}
// 2.2 UPDATE THE NONCE
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
senderState.incrementNonce();
@ -298,14 +301,17 @@ public class Program {
chainDB.startTrack();
stateDB.startTrack();
// todo: update the balance/value simple transfer
// todo: check if the endowment can really be done
receiverState.addToBalance(endowmentValue.value());
stateDB.update(toAddress, receiverState.getEncoded());
Map<DataWord, DataWord> storage = null;
if (details != null)
storage = details.getStorage();
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(this, toAddressDW, storage, endowmentValue, gas,receiverState.getBalance(),
ProgramInvokeFactory.createProgramInvoke(this, toAddressDW, storage,
endowmentValue, gas, receiverState.getBalance(),
data.array(),
detailDB, chainDB, stateDB);
@ -326,7 +332,8 @@ public class Program {
}
// todo: apply results: result.gethReturn()
// todo: refund for remain gas
// todo: if there is out specified place hReturn on the out
detailDB.commitTrack();
chainDB.commitTrack();
@ -334,7 +341,7 @@ public class Program {
stackPush(new DataWord(1));
// the gas spent in any internal outcome
spendGas(result.getGasUsed());
spendGas(result.getGasUsed(), " 'Total for CALL run' ");
logger.info("The usage of the gas in external call updated", result.getGasUsed());
// update the storage , it could
@ -348,7 +355,9 @@ public class Program {
}
public void spendGas(int gasValue){
public void spendGas(int gasValue, String cause){
gasLogger.info("Spent: for cause={} gas={}", cause, gasValue);
long afterSpend = invokeData.getGas().longValue() - gasValue - result.getGasUsed();
if (afterSpend < 0)

View File

@ -38,5 +38,7 @@ public interface ProgramInvoke {
public TrackDatabase getChainDb();
public TrackTrie getStateDb();
public boolean byTransaction();
}

View File

@ -4,6 +4,7 @@ import org.ethereum.db.TrackDatabase;
import org.ethereum.trie.TrackTrie;
import org.ethereum.util.ByteUtil;
import java.util.Arrays;
import java.util.Map;
/**
@ -38,6 +39,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
TrackDatabase detaildDB;
TrackDatabase chainDb;
TrackTrie stateDb;
private boolean byTransaction = true;
public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance,
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
@ -68,6 +70,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
this.detaildDB = detaildDB;
this.chainDb = chainDb;
this.stateDb = stateDB;
this.byTransaction = false;
}
@ -234,4 +237,63 @@ public class ProgramInvokeImpl implements ProgramInvoke {
public TrackTrie getStateDb() {
return stateDb;
}
@Override
public boolean byTransaction() {
return byTransaction;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProgramInvokeImpl that = (ProgramInvokeImpl) o;
if (byTransaction != that.byTransaction) return false;
if (address != null ? !address.equals(that.address) : that.address != null) return false;
if (balance != null ? !balance.equals(that.balance) : that.balance != null) return false;
if (callValue != null ? !callValue.equals(that.callValue) : that.callValue != null) return false;
if (caller != null ? !caller.equals(that.caller) : that.caller != null) return false;
if (chainDb != null ? !chainDb.equals(that.chainDb) : that.chainDb != null) return false;
if (coinbase != null ? !coinbase.equals(that.coinbase) : that.coinbase != null) return false;
if (detaildDB != null ? !detaildDB.equals(that.detaildDB) : that.detaildDB != null) return false;
if (difficulty != null ? !difficulty.equals(that.difficulty) : that.difficulty != null) return false;
if (gas != null ? !gas.equals(that.gas) : that.gas != null) return false;
if (gasPrice != null ? !gasPrice.equals(that.gasPrice) : that.gasPrice != null) return false;
if (gaslimit != null ? !gaslimit.equals(that.gaslimit) : that.gaslimit != null) return false;
if (!Arrays.equals(msgData, that.msgData)) return false;
if (number != null ? !number.equals(that.number) : that.number != null) return false;
if (origin != null ? !origin.equals(that.origin) : that.origin != null) return false;
if (prevHash != null ? !prevHash.equals(that.prevHash) : that.prevHash != null) return false;
if (stateDb != null ? !stateDb.equals(that.stateDb) : that.stateDb != null) return false;
if (storage != null ? !storage.equals(that.storage) : that.storage != null) return false;
if (timestamp != null ? !timestamp.equals(that.timestamp) : that.timestamp != null) return false;
return true;
}
@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 + (detaildDB != null ? detaildDB.hashCode() : 0);
result = 31 * result + (chainDb != null ? chainDb.hashCode() : 0);
result = 31 * result + (stateDb != null ? stateDb.hashCode() : 0);
result = 31 * result + (byTransaction ? 1 : 0);
return result;
}
}

View File

@ -65,24 +65,24 @@ public class VM {
switch (OpCode.code(op)) {
case SHA3:
program.spendGas(GasCost.SHA3);
program.spendGas(GasCost.SHA3, OpCode.code(op).name());
break;
case SLOAD:
program.spendGas(GasCost.SLOAD);
program.spendGas(GasCost.SLOAD, OpCode.code(op).name());
break;
case SSTORE:
break;
case BALANCE:
program.spendGas(GasCost.BALANCE);
program.spendGas(GasCost.BALANCE, OpCode.code(op).name());
break;
case CREATE:
program.spendGas(GasCost.CREATE);
program.spendGas(GasCost.CREATE, OpCode.code(op).name());
break;
case CALL:
program.spendGas(GasCost.CALL);
program.spendGas(GasCost.CALL, OpCode.code(op).name());
break;
default:
program.spendGas(GasCost.STEP);
program.spendGas(GasCost.STEP, OpCode.code(op).name());
break;
}
@ -458,11 +458,11 @@ public class VM {
DataWord oldValue = program.storageLoad(addr);
program.storageSave(addr, value);
if (oldValue == null && !value.isZero()){
program.spendGas(GasCost.SSTORE * 2);
program.spendGas(GasCost.SSTORE * 2, OpCode.code(op).name());
} else if (oldValue != null && value.isZero()){
program.spendGas(GasCost.SSTORE * 0);
program.spendGas(GasCost.SSTORE * 0, OpCode.code(op).name());
} else
program.spendGas(GasCost.SSTORE);
program.spendGas(GasCost.SSTORE, OpCode.code(op).name());
program.step();
} break;
case JUMP:{
@ -554,7 +554,10 @@ public class VM {
// memory gas calc
int newMemSize = program.getMemSize();
program.spendGas(GasCost.MEMORY * (newMemSize - oldMemSize) /32);
int memoryUsage = (newMemSize - oldMemSize) /32;
if (memoryUsage > 0)
program.spendGas(GasCost.MEMORY * memoryUsage, OpCode.code(op).name());
program.fullTrace();
} catch (RuntimeException e) {

View File

@ -5,9 +5,10 @@ server.acceptConnections = false
# one default access point to start
# discover the network e.g. ip: [54.201.28.117] port: [30303]
# Peer Server Zero: peer discovery
peer.discovery.ip = 54.201.28.117
peer.discovery.ip = 54.72.69.180
peer.discovery.port = 30303
# Peer Server One: peer discovery
#peer.discovery.ip = 54.204.10.41
#peer.discovery.port = 30303
@ -16,8 +17,8 @@ peer.discovery.port = 30303
# that is the peer through
# we get the chain: [54.201.28.117] port: [30303]
# ZeroGox
peer.active.ip = 54.204.10.41
peer.active.port = 30303
#peer.active.ip = 54.204.10.41
#peer.active.port = 30303
# Some dude in Canada
#peer.active.ip = 131.104.247.135
@ -29,8 +30,12 @@ peer.active.port = 30303
# RomanJ general
#peer.active.ip = 54.211.14.10
#peer.active.port = 50505
peer.active.ip = 54.211.14.10
peer.active.port = 30303
#poc5.testnet.ethereum.org
#peer.active.ip = 54.72.69.180
#peer.active.port = 30303
#peer.active.ip = 151.64.223.120
#peer.active.port = 30304
@ -48,7 +53,7 @@ peer.discovery = true
# number of workers that
# tastes the peers for being
# online [1..10]
peer.discovery.workers = 5
peer.discovery.workers = 15
# connection timeout for trying to
# connect to a peer [seconds]
@ -59,13 +64,13 @@ peer.discovery.timeout = 2
# transaction got approved when
# include into a transactions msg
# retrieved from the peer [seconds]
transaction.approve.timeout = 360
transaction.approve.timeout = 15
# the parameter specifies how much
# time the active peer will wait
# for a message to come before kill
# the channel
active.peer.channel.timeout = 360
active.peer.channel.timeout = 15
# default directory where we keep
# basic Serpent samples relative

View File

@ -1,8 +1,10 @@
package org.ethereum.vm;
import org.ethereum.core.ContractDetails;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.TrackDatabase;
import org.ethereum.manager.WorldManager;
import org.ethereum.trie.TrackTrie;
import org.spongycastle.util.encoders.Hex;
@ -18,6 +20,13 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
byte[] msgData;
TrackTrie stateDB = null;
TrackDatabase chainDb = null;
TrackDatabase detaildDB = null;
ContractDetails details = null;
public ProgramInvokeMockImpl(byte[] msgDataRaw){
this.msgData = msgDataRaw;
@ -29,9 +38,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
/* ADDRESS op */
public DataWord getOwnerAddress(){
byte[] cowPrivKey = HashUtil.sha3("cow".getBytes());
byte[] addr = ECKey.fromPrivate(cowPrivKey).getAddress();
byte[] addr = Hex.decode("77045e71a7a2c50903d88e564cd72fab11e82051");
return new DataWord(addr);
}
@ -69,7 +76,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
/* GAS op */
public DataWord getGas() {
byte[] minGasPrice = Hex.decode("03E8");
byte[] minGasPrice = Hex.decode("0F4240");
return new DataWord(minGasPrice);
}
@ -159,27 +166,50 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
@Override
public DataWord getGaslimit() {
long gasLimit = 968269;
long gasLimit = 1000000;
return new DataWord(gasLimit);
}
public void setStateDB(TrackTrie stateDB) {
this.stateDB = stateDB;
}
public void setChainDb(TrackDatabase chainDb) {
this.chainDb = chainDb;
}
public void setDetaildDB(TrackDatabase detaildDB) {
this.detaildDB = detaildDB;
}
public void setDetails(ContractDetails details) {
this.details = details;
}
@Override
public Map<DataWord, DataWord> getStorage() {
return null;
if (details == null) return null;
return details.getStorage();
}
@Override
public TrackDatabase getDetaildDB() {
return null;
return detaildDB;
}
@Override
public TrackDatabase getChainDb() {
return null;
return chainDb;
}
@Override
public TrackTrie getStateDb() {
return null;
return stateDB;
}
@Override
public boolean byTransaction() {
return true;
}
}

View File

@ -0,0 +1,107 @@
package org.ethereum.vm;
import org.ethereum.core.AccountState;
import org.ethereum.core.ContractDetails;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.TrackDatabase;
import org.ethereum.manager.WorldManager;
import org.ethereum.trie.TrackTrie;
import org.junit.Test;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.HashMap;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 16/06/2014 10:37
*/
public class VMComplexTest {
@Test // contract call recursive
public void test1(){
/**
* #The code will run
* ------------------
a = contract.storage[999]
if a > 0:
contract.storage[999] = a - 1
# call to contract: 77045e71a7a2c50903d88e564cd72fab11e82051
send((tx.gas / 10 * 8), 0x77045e71a7a2c50903d88e564cd72fab11e82051, 0)
else:
stop
*/
DataWord key1 = new DataWord(999);
DataWord value1 = new DataWord(3);
HashMap<DataWord, DataWord> storage = new HashMap<>();
storage.put(key1, value1);
ContractDetails contractDetails = new ContractDetails(storage);
// Set contract into Database
String callerAddr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
String contractAddr = "77045e71a7a2c50903d88e564cd72fab11e82051";
String code = "6103e75660005460006000530b0f630000004b596001600053036103e757600060006000600060007377045e71a7a2c50903d88e564cd72fab11e820516008600a5c0402f1630000004c5800";
byte[] contractAddrB = Hex.decode(contractAddr);
byte[] callerAddrB = Hex.decode(callerAddr);
byte[] codeB = Hex.decode(code);
byte[] codeKey = HashUtil.sha3(codeB);
AccountState accountState = new AccountState();
accountState.setCodeHash(codeKey);
AccountState callerAcountState = new AccountState();
callerAcountState.addToBalance(new BigInteger("100000000000000000000"));
WorldManager.instance.worldState.update(callerAddrB, callerAcountState.getEncoded());
WorldManager.instance.worldState.update(contractAddrB, accountState.getEncoded());
WorldManager.instance.chainDB.put(codeKey, codeB);
WorldManager.instance.detaildDB.put(contractAddrB, contractDetails.getEncoded());
TrackTrie stateDB = new TrackTrie(WorldManager.instance.worldState);
TrackDatabase chainDb = new TrackDatabase(WorldManager.instance.chainDB);
TrackDatabase detaildDB = new TrackDatabase(WorldManager.instance.detaildDB);
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setDetaildDB(detaildDB);
pi.setChainDb(chainDb);
pi.setStateDB(stateDB);
pi.setDetails(contractDetails);
// Play the program
VM vm = new VM();
Program program = new Program(codeB, pi);
try {
while(!program.isStopped())
vm.step(program);
} catch (RuntimeException e) {
program.setRuntimeFailure(e);
}
System.out.println("============ Results ============");
AccountState as =
new AccountState(WorldManager.instance.worldState.get(
Hex.decode( contractAddr) ));
System.out.println("*** Used gas: " + program.result.getGasUsed());
System.out.println("*** Contract Balance: " + as.getBalance());
}
}

View File

@ -2544,7 +2544,7 @@ public class VMTest {
Program program =
new Program(Hex.decode("30"),
createProgramInvoke_1());
String s_expected_1 = "000000000000000000000000CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826";
String s_expected_1 = "00000000000000000000000077045E71A7A2C50903D88E564CD72FAB11E82051";
vm.step(program);

View File

@ -0,0 +1,23 @@
# Root logger option
log4j.rootLogger=DEBUG, stdout
# 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
# filter noisy classes
log4j.logger.org.ethereum.net = FATAL
log4j.logger.peerdiscovery = FATAL
log4j.logger.java.nio = FATAL
log4j.logger.io.netty = FATAL
log4j.logger.org.ethereum.core = FATAL
log4j.logger.wire = FATAL
log4j.logger.VM = FATAL
log4j.logger.main = FATAL
log4j.logger.state = FATAL
log4j.logger.blockchain = FATAL
log4j.logger.ui = FATAL
log4j.logger.gas = DEBUG

View File

@ -0,0 +1,89 @@
# if the system will work as a server also
# accept for incoming connections [true/false]
server.acceptConnections = false
# one default access point to start
# discover the network e.g. ip: [54.201.28.117] port: [30303]
# Peer Server Zero: peer discovery
peer.discovery.ip = 54.201.28.117
peer.discovery.port = 30303
# Peer Server One: peer discovery
#peer.discovery.ip = 54.204.10.41
#peer.discovery.port = 30303
# active peer ip and port
# that is the peer through
# we get the chain: [54.201.28.117] port: [30303]
# ZeroGox
#peer.active.ip = 54.204.10.41
#peer.active.port = 30303
# Some dude in Canada
#peer.active.ip = 131.104.247.135
#peer.active.port = 30303
# Nick
#peer.active.ip = 82.217.72.169
#peer.active.port = 30303
# RomanJ general
peer.active.ip = 54.211.14.10
peer.active.port = 50505
#poc5.testnet.ethereum.org
#peer.active.ip = 54.72.69.180
#peer.active.port = 30303
#peer.active.ip = 151.64.223.120
#peer.active.port = 30304
# specify if the mechanism
# to discover more and more
# peers and check the already
# discovered peers is on
# if peer discovery is off
# the peer window will show
# only what retrieved by active
# peer [true/false]
peer.discovery = true
# number of workers that
# tastes the peers for being
# online [1..10]
peer.discovery.workers = 15
# connection timeout for trying to
# connect to a peer [seconds]
peer.discovery.timeout = 2
# the time we wait to the network
# to approve the transaction, the
# transaction got approved when
# include into a transactions msg
# retrieved from the peer [seconds]
transaction.approve.timeout = 15
# the parameter specifies how much
# time the active peer will wait
# for a message to come before kill
# the channel
active.peer.channel.timeout = 15
# default directory where we keep
# basic Serpent samples relative
# to home.dir
samples.dir = samples
# everytime the application starts
# the existing database will be
# destroyed and all the data will be
# downloaded from peers again
database.reset = true
# this string is computed
# to be eventually the address
# that get the miner reward
coinbase.secret = "monkey"