JSON Testing introduced:

+ JSON defined tests to run
+ VM fixes and adaptation for recent changes
This commit is contained in:
romanman 2014-07-03 16:58:40 +01:00
parent 33be499ba6
commit 14ba667d46
18 changed files with 1305 additions and 76 deletions

View File

@ -3,6 +3,7 @@ package org.ethereum.db;
import java.util.Arrays;
import org.ethereum.util.FastByteComparisons;
import org.spongycastle.util.encoders.Hex;
/**
* www.ethereumJ.com
@ -45,4 +46,9 @@ public class ByteArrayWrapper implements Comparable<ByteArrayWrapper> {
public byte[] getData() {
return data;
}
@Override
public String toString() {
return Hex.toHexString(data);
}
}

View File

@ -138,8 +138,10 @@ public class Repository {
public BigInteger addBalance(byte[] address, BigInteger value) {
AccountState state = getAccountState(address);
if (state == null)
return BigInteger.ZERO;
if (state == null){
state = createAccount(address);
}
BigInteger newBalance = state.addToBalance(value);
@ -295,7 +297,8 @@ public class Repository {
byte[] code = details.getCode();
Map<DataWord, DataWord> storage = details.getStorage();
String accountLine = JSONHelper.dumpLine(key.getData(), nonce.toByteArray(),
String accountLine = JSONHelper.dumpLine(key.getData(),
nonce.toByteArray(),
balance.toByteArray(), stateRoot, codeHash, code, storage);
bw.write(accountLine);

View File

@ -0,0 +1,110 @@
package org.ethereum.jsontestsuite;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.util.ByteUtil;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 28/06/2014 10:25
*/
public class AccountState {
byte[] address;
byte[] balance;
byte[] code;
byte[] nonce;
Map<ByteArrayWrapper, ByteArrayWrapper> storage = new HashMap<>();
public AccountState(byte[] address, JSONObject accountState) {
this.address = address;
String balance = accountState.get("balance").toString();
JSONArray code = (JSONArray)accountState.get("code");
String nonce = accountState.get("nonce").toString();
JSONObject store = (JSONObject)accountState.get("storage");
this.balance = new BigInteger(balance).toByteArray();
this.code = Helper.parseDataArray(code);
this.nonce = new BigInteger(nonce).toByteArray();
int size = store.keySet().size();
Object[] keys = store.keySet().toArray();
for (int i = 0; i < size; ++i){
String keyS = keys[i].toString();
Object valObject = store.get(keys[i]);
byte[] valBytes = Helper.parseDataArray((JSONArray)valObject);
ByteArrayWrapper key;
boolean hexVal = Pattern.matches("0[xX][0-9a-fA-F]+", keyS);
if (hexVal){
key = new ByteArrayWrapper( Hex.decode(keyS.substring(2)) );
} else {
byte[] data = ByteUtil.bigIntegerToBytes( new BigInteger(keyS) );
key = new ByteArrayWrapper( data );
}
ByteArrayWrapper value =
new ByteArrayWrapper(valBytes);
storage.put(key, value);
}
}
public byte[] getAddress() {
return address;
}
public byte[] getBalance() {
return balance;
}
public BigInteger getBigIntegerBalance() {
return new BigInteger(balance);
}
public byte[] getCode() {
return code;
}
public byte[] getNonce() {
return nonce;
}
public long getNonceLong() {
return new BigInteger(nonce).longValue();
}
public Map<ByteArrayWrapper, ByteArrayWrapper> getStorage() {
return storage;
}
@Override
public String toString() {
return "AccountState{" +
"address=" + Hex.toHexString(address) +
", balance=" + Hex.toHexString(balance) +
", code=" + Hex.toHexString(code) +
", nonce=" + Hex.toHexString(nonce) +
", storage=" + storage +
'}';
}
}

View File

@ -0,0 +1,76 @@
package org.ethereum.jsontestsuite;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.util.ByteUtil;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 28/06/2014 10:25
*/
public class CallCreate {
byte[] data;
byte[] destination;
byte[] gasLimit;
byte[] value;
/* e.g.
"data" : [
],
"destination" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"gasLimit" : 9792,
"value" : 74
*/
public CallCreate(JSONObject callCreateJSON) {
JSONArray data = (JSONArray)callCreateJSON.get("data");
String destination = (String)callCreateJSON.get("destination");
Long gasLimit = (Long)callCreateJSON.get("gasLimit");
Long value = (Long)callCreateJSON.get("value");
this.data = Helper.parseDataArray(data);
this.destination = Hex.decode(destination);
this.gasLimit = ByteUtil.bigIntegerToBytes( BigInteger.valueOf(gasLimit) );
this.value = ByteUtil.bigIntegerToBytes( BigInteger.valueOf(value) );
}
public byte[] getData() {
return data;
}
public byte[] getDestination() {
return destination;
}
public byte[] getGasLimit() {
return gasLimit;
}
public byte[] getValue() {
return value;
}
@Override
public String toString() {
return "CallCreate{" +
"data=" + Hex.toHexString(data) +
", destination=" + Hex.toHexString(destination) +
", gasLimit=" + Hex.toHexString(gasLimit) +
", value=" + Hex.toHexString(value) +
'}';
}
}

View File

@ -0,0 +1,89 @@
package org.ethereum.jsontestsuite;
import org.ethereum.util.ByteUtil;
import org.json.simple.JSONObject;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Arrays;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 28/06/2014 10:23
*/
public class Env {
private byte[] currentCoinbase;
private byte[] currentDifficlty;
private byte[] currentGasLimit;
private byte[] currentNumber;
private byte[] currentTimestamp;
private byte[] previousHash;
/*
e.g:
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
*/
public Env(JSONObject env) {
String coinbase = env.get("currentCoinbase").toString();
String difficulty = env.get("currentDifficulty").toString();
String timestamp = env.get("currentTimestamp").toString();
String number = env.get("currentNumber").toString();
String gasLimit = env.get("currentGasLimit").toString();
String prevHash = env.get("previousHash").toString();
this.currentCoinbase = Hex.decode(coinbase);
this.currentDifficlty = ByteUtil.bigIntegerToBytes( new BigInteger(difficulty) );
this.currentGasLimit = ByteUtil.bigIntegerToBytes( new BigInteger(gasLimit) );
this.currentNumber = ByteUtil.bigIntegerToBytes( new BigInteger(number) );
this.currentTimestamp = ByteUtil.bigIntegerToBytes( new BigInteger(timestamp) );
this.previousHash = Hex.decode(prevHash);
}
public byte[] getCurrentCoinbase() {
return currentCoinbase;
}
public byte[] getCurrentDifficlty() {
return currentDifficlty;
}
public byte[] getCurrentGasLimit() {
return currentGasLimit;
}
public byte[] getCurrentNumber() {
return currentNumber;
}
public byte[] getCurrentTimestamp() {
return currentTimestamp;
}
public byte[] getPreviousHash() {
return previousHash;
}
@Override
public String toString() {
return "Env{" +
"currentCoinbase=" + Hex.toHexString(currentCoinbase) +
", currentDifficlty=" + Hex.toHexString(currentDifficlty) +
", currentGasLimit=" + Hex.toHexString(currentGasLimit) +
", currentNumber=" + Hex.toHexString(currentNumber) +
", currentTimestamp=" + Hex.toHexString(currentTimestamp) +
", previousHash=" + Hex.toHexString(previousHash) +
'}';
}
}

View File

@ -0,0 +1,114 @@
package org.ethereum.jsontestsuite;
import org.ethereum.util.ByteUtil;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Arrays;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 28/06/2014 10:23
*/
public class Exec {
private byte[] address;
private byte[] caller;
private byte[] data;
private byte[] code;
private byte[] gas;
private byte[] gasPrice;
private byte[] origin;
private byte[] value;
/*
e.g:
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"data" : [
],
"code" : [ 96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241 ],
"gas" : 10000,
"gasPrice" : 100000000000000,
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : 1000000000000000000
*/
public Exec(JSONObject exec) {
String address = exec.get("address").toString();
String caller = exec.get("caller").toString();
String gas = exec.get("gas").toString();
String gasPrice = exec.get("gasPrice").toString();
String origin = exec.get("origin").toString();
String value = exec.get("value").toString();
this.address = Hex.decode(address);
this.caller = Hex.decode(caller);
this.data = Helper.parseDataArray((JSONArray) exec.get("data"));
this.code = Helper.parseDataArray((JSONArray) exec.get("code"));
this.gas = ByteUtil.bigIntegerToBytes( new BigInteger(gas) );
this.gasPrice = ByteUtil.bigIntegerToBytes( new BigInteger(gasPrice) );
this.origin = Hex.decode(origin);
this.value = ByteUtil.bigIntegerToBytes(new BigInteger(value));
}
public byte[] getAddress() {
return address;
}
public byte[] getCaller() {
return caller;
}
public byte[] getData() {
return data;
}
public byte[] getCode() {
return code;
}
public byte[] getGas() {
return gas;
}
public byte[] getGasPrice() {
return gasPrice;
}
public byte[] getOrigin() {
return origin;
}
public byte[] getValue() {
return value;
}
@Override
public String toString() {
return "Exec{" +
"address=" + Hex.toHexString(address) +
", caller=" + Hex.toHexString(caller) +
", data=" + Hex.toHexString(data) +
", code=" + Hex.toHexString(data) +
", gas=" + Hex.toHexString(gas) +
", gasPrice=" + Hex.toHexString(gasPrice) +
", origin=" + Hex.toHexString(origin) +
", value=" + Hex.toHexString(value) +
'}';
}
}

View File

@ -0,0 +1,66 @@
package org.ethereum.jsontestsuite;
import org.ethereum.util.ByteUtil;
import org.json.simple.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.regex.Pattern;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 28/06/2014 11:59
*/
public class Helper {
private static Logger logger = LoggerFactory.getLogger("misc");
public static byte[] parseDataArray(JSONArray valArray){
// value can be:
// 1. 324234 number
// 2. "0xAB3F23A" - hex string
// 3. "239472398472" - big number
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (int i = 0; i < valArray.size(); ++i){
Object val = valArray.get(i);
if (val instanceof String){
// Hex num
boolean hexVal = Pattern.matches("0[xX][0-9a-fA-F]+", val.toString());
if (hexVal){
String number = ((String) val).substring(2);
if (number.length() % 2 == 1) number = "0" + number;
byte[] data = Hex.decode(number);
try {bos.write(data);} catch (IOException e) { logger.error("should not happen", e);}
} else{
// BigInt num
boolean isNumeric = Pattern.matches("[0-9a-fA-F]+", val.toString());
if (!isNumeric) throw new Error("Wrong test case JSON format");
else{
BigInteger value = new BigInteger(val.toString());
try {bos.write(value.toByteArray());} catch (IOException e)
{ logger.error("should not happen", e);}
}
}
} else if (val instanceof Long) {
// Simple long
byte[] data = ByteUtil.bigIntegerToBytes( BigInteger.valueOf((Long)val) );
try {bos.write(data);} catch (IOException e) { logger.error("should not happen", e);}
} else {
throw new Error("Wrong test case JSON format");
}
}
return bos.toByteArray();
}
}

View File

@ -0,0 +1,131 @@
package org.ethereum.jsontestsuite;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.util.ByteUtil;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.*;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 28/06/2014 10:22
*/
public class TestCase {
// "env": { ... },
private Env env;
// "exec": { ... },
private Exec exec;
// "gas": { ... },
private byte[] gas;
// "out": { ... },
private byte[] out;
// "pre": { ... },
private Map<ByteArrayWrapper, AccountState> pre = new HashMap<>();
// "post": { ... },
private Map<ByteArrayWrapper, AccountState> post = new HashMap<>();
// "callcreates": { ... }
private List<CallCreate> callCreateList = new ArrayList<>();
public TestCase(JSONObject testCaseJSONObj) throws ParseException{
try {
JSONObject envJSON = (JSONObject)testCaseJSONObj.get("env");
JSONObject execJSON = (JSONObject)testCaseJSONObj.get("exec");
JSONObject preJSON = (JSONObject)testCaseJSONObj.get("pre");
JSONObject postJSON = (JSONObject)testCaseJSONObj.get("post");
JSONArray callCreates = (JSONArray)testCaseJSONObj.get("callcreates");
Long gasNum = (Long)testCaseJSONObj.get("gas");
this.gas = ByteUtil.bigIntegerToBytes(BigInteger.valueOf(gasNum));
this.out = Helper.parseDataArray((JSONArray) testCaseJSONObj.get("out"));
for (Object key : preJSON.keySet()){
byte[] keyBytes = Hex.decode(key.toString());
AccountState accountState =
new AccountState(keyBytes, (JSONObject) preJSON.get(key));
pre.put(new ByteArrayWrapper(keyBytes), accountState);
}
for (Object key : postJSON.keySet()){
byte[] keyBytes = Hex.decode(key.toString());
AccountState accountState =
new AccountState(keyBytes, (JSONObject) postJSON.get(key));
post.put(new ByteArrayWrapper(keyBytes), accountState);
}
for (int i = 0; i < callCreates.size(); ++i){
CallCreate cc = new CallCreate((JSONObject)callCreates.get(i));
this.callCreateList.add(cc);
}
this.env = new Env(envJSON);
this.exec = new Exec(execJSON);
} catch (Throwable e) {
throw new ParseException(0, e);
}
}
public Env getEnv() {
return env;
}
public Exec getExec() {
return exec;
}
public byte[] getGas() {
return gas;
}
public byte[] getOut() {
return out;
}
public Map<ByteArrayWrapper, AccountState> getPre() {
return pre;
}
public Map<ByteArrayWrapper, AccountState> getPost() {
return post;
}
public List<CallCreate> getCallCreateList() {
return callCreateList;
}
@Override
public String toString() {
return "TestCase{" +
"" + env +
", " + exec +
", gas=" + Hex.toHexString(gas) +
", out=" + Hex.toHexString(out) +
", pre=" + pre +
", post=" + post +
", callcreates=" + callCreateList +
'}';
}
}

View File

@ -0,0 +1,287 @@
package org.ethereum.jsontestsuite;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.db.ContractDetails;
import org.ethereum.db.Repository;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 02/07/2014 13:03
*/
public class TestRunner {
private Logger logger = LoggerFactory.getLogger("JSONTest");
private List<String> results = new ArrayList<>();
public void runTestCase(TestCase testCase){
Repository repository = new Repository();
/* 1. Store pre-exist accounts - Pre */
for (ByteArrayWrapper key : testCase.getPre().keySet()){
AccountState accountState = testCase.getPre().get(key);
repository.createAccount(key.getData());
repository.saveCode(key.getData(), accountState.getCode());
repository.addBalance(key.getData(), new BigInteger(accountState.getBalance()));
for (long i = 0; i < accountState.getNonceLong(); ++i)
repository.increaseNonce(key.getData());
}
/* 2. Create ProgramInvoke - Env/Exec */
Env env = testCase.getEnv();
Exec exec = testCase.getExec();
byte[] address = exec.getAddress();
byte[] origin = exec.getOrigin();
byte[] caller = exec.getCaller();
byte[] balance = ByteUtil.bigIntegerToBytes(repository.getBalance(exec.getAddress()));
byte[] gasPrice = exec.getGasPrice();
byte[] gas = exec.getGas();
byte[] callValue = exec.getValue();
byte[] msgData = exec.getData();
byte[] lastHash = env.getPreviousHash();
byte[] coinbase = env.getCurrentCoinbase();
long timestamp = new BigInteger(env.getCurrentTimestamp()).longValue();
long number = new BigInteger(env.getCurrentNumber()).longValue();
byte[] difficulty = env.getCurrentDifficlty();
long gaslimit = new BigInteger(env.getCurrentGasLimit()).longValue();
ProgramInvoke programInvoke = new ProgramInvokeImpl(address, origin, caller, balance,
gasPrice, gas, callValue, msgData, lastHash, coinbase,
timestamp, number, difficulty, gaslimit, repository, true);
/* 3. Create Program - exec.code */
/* 4. run VM */
VM vm = new VM();
Program program = new Program(exec.getCode(), programInvoke);
try {
while(!program.isStopped())
vm.step(program);
} catch (RuntimeException e) {
program.setRuntimeFailure(e);
}
/* 5. Assert Post values */
for (ByteArrayWrapper key : testCase.getPost().keySet()){
AccountState accountState = testCase.getPost().get(key);
long expectedNonce = accountState.getNonceLong();
BigInteger expectedBalance = accountState.getBigIntegerBalance();
byte[] expectedCode = accountState.getCode();
boolean accountExist = (null != repository.getAccountState(key.getData()));
if (!accountExist){
String output =
String.format("The expected account does not exist. key: [ %s ]", Hex.toHexString(key.getData()));
logger.info(output);
results.add(output);
continue;
}
long actualNonce = repository.getNonce(key.getData()).longValue();
BigInteger actualBalance = repository.getBalance(key.getData());
byte[] actualCode = repository.getCode(key.getData());
if (actualCode == null) actualCode = "".getBytes();
if (expectedNonce != actualNonce){
String output =
String.format("The nonce result is different. key: [ %s ], expectedNonce: [ %d ] is actualNonce: [ %d ] ",
Hex.toHexString(key.getData()), expectedNonce, actualNonce);
logger.info(output);
results.add(output);
}
if (!expectedBalance.equals(actualBalance)){
String output =
String.format("The balance result is different. key: [ %s ], expectedBalance: [ %s ] is actualBalance: [ %s ] ",
Hex.toHexString(key.getData()), expectedBalance.toString(), actualBalance.toString());
logger.info(output);
results.add(output);
}
if (!Arrays.equals(expectedCode, actualCode)){
String output =
String.format("The code result is different. key: [ %s ], expectedCode: [ %s ] is actualCode: [ %s ] ",
Hex.toHexString(key.getData()),
Hex.toHexString(expectedCode),
Hex.toHexString(actualCode));
logger.info(output);
results.add(output);
}
// assert storage
Map<ByteArrayWrapper, ByteArrayWrapper> storage = accountState.getStorage();
for (ByteArrayWrapper storageKey : storage.keySet() ){
byte[] expectedStValue = storage.get(storageKey).getData();
ContractDetails contractDetails =
program.getResult().getRepository().getContractDetails(storageKey.getData());
if (contractDetails == null){
String output =
String.format("Storage raw doesn't exist: key [ %s ], expectedValue: [ %s ]",
Hex.toHexString(storageKey.getData()),
Hex.toHexString(expectedStValue)
);
logger.info(output);
results.add(output);
continue;
}
Map<DataWord, DataWord> testStorage = contractDetails.getStorage();
DataWord actualValue = testStorage.get(new DataWord(storageKey.getData()));
if (!Arrays.equals(expectedStValue, actualValue.getNoLeadZeroesData())){
String output =
String.format("Storage value different: key [ %s ], expectedValue: [ %s ], actualValue: [ %s ]",
Hex.toHexString(storageKey.getData()),
Hex.toHexString(actualValue.getData()),
Hex.toHexString(expectedStValue)
);
logger.info(output);
results.add(output);
}
}
}
List<org.ethereum.vm.CallCreate> resultCallCreates =
program.getResult().getCallCreateList();
// assert call creates
for (int i = 0; i < testCase.getCallCreateList().size(); ++i){
org.ethereum.vm.CallCreate resultCallCreate = null;
if (resultCallCreates != null && resultCallCreates.size() > i){
resultCallCreate = resultCallCreates.get(i);
}
CallCreate expectedCallCreate =
testCase.getCallCreateList().get(i);
if (resultCallCreate == null && expectedCallCreate != null){
String output =
String.format("Missing call/create invoke: to: [ %s ], data: [ %s ], gas: [ %s ], value: [ %s ]",
Hex.toHexString(expectedCallCreate.getDestination()),
Hex.toHexString(expectedCallCreate.getData()),
Hex.toHexString(expectedCallCreate.getGasLimit()),
Hex.toHexString(expectedCallCreate.getValue()));
logger.info(output);
results.add(output);
continue;
}
boolean assertDestination = Arrays.equals(expectedCallCreate.getDestination(),
resultCallCreate.getDestination());
if (!assertDestination){
String output =
String.format("Call/Create destination is different expected: [ %s ], result: [ %s ]",
Hex.toHexString(expectedCallCreate.getDestination()),
Hex.toHexString(resultCallCreate.getDestination()));
logger.info(output);
results.add(output);
}
boolean assertData = Arrays.equals(expectedCallCreate.getData(),
resultCallCreate.getData());
if (!assertData){
String output =
String.format("Call/Create data is different expected: [ %s ], result: [ %s ]",
Hex.toHexString( expectedCallCreate.getData() ),
Hex.toHexString(resultCallCreate.getData()) );
logger.info(output);
results.add(output);
}
boolean assertGasLimit = Arrays.equals(expectedCallCreate.getGasLimit(),
resultCallCreate.getGasLimit());
if (!assertGasLimit){
String output =
String.format("Call/Create gasLimit is different expected: [ %s ], result: [ %s ]",
Hex.toHexString( expectedCallCreate.getGasLimit() ),
Hex.toHexString( resultCallCreate.getGasLimit()) );
logger.info(output);
results.add(output);
}
boolean assertValue = Arrays.equals(expectedCallCreate.getValue(),
resultCallCreate.getValue());
if (!assertValue){
String output =
String.format("Call/Create value is different expected: [ %s ], result: [ %s ]",
Hex.toHexString( expectedCallCreate.getValue() ),
Hex.toHexString( resultCallCreate.getValue() ));
logger.info(output);
results.add(output);
}
}
// assert out
byte[] expectedHReturn = testCase.getOut();
byte[] actualHReturn = new byte[0];
if (program.getResult().getHReturn() != null){
actualHReturn = program.getResult().getHReturn().array();
}
if (!Arrays.equals(expectedHReturn, actualHReturn)){
String output =
String.format("HReturn is differnt expected hReturn: [ %s ], actual hReturn: [ %s ]",
Hex.toHexString( expectedHReturn ),
Hex.toHexString( actualHReturn ));
logger.info(output);
results.add(output);
}
// assert gas
BigInteger expectedGas = new BigInteger(testCase.getGas());
BigInteger actualGas = new BigInteger(gas).subtract(BigInteger.valueOf(program.getResult().getGasUsed()));
if (!expectedGas.equals(actualGas)){
String output =
String.format("HReturn is differnt expected hReturn: [ %s ], actual hReturn: [ %s ]",
expectedGas.toString() ,
actualGas.toString());
logger.info(output);
results.add(output);
}
program.getResult().getRepository().close();
}
}

View File

@ -37,6 +37,27 @@ public class ByteUtil {
System.arraycopy(biBytes, start, bytes, numBytes - length, length);
return bytes;
}
/**
* emitting sign indication byte
*
* @param b - any big integer number
* @return
*/
public static byte[] bigIntegerToBytes(BigInteger b) {
if (b == null)
return null;
byte[] data = b.toByteArray();
if (data.length != 1 && data[0] == 0) {
byte[] tmp = new byte[data.length - 1];
System.arraycopy(data, 1, tmp, 0, tmp.length);
data = tmp;
}
return data;
}
public static byte[] longToBytes(long l) {
return ByteBuffer.allocate(8).putLong(l).array();

View File

@ -0,0 +1,40 @@
package org.ethereum.vm;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 03/07/2014 08:29
*/
public class CallCreate {
byte[] data;
byte[] destination;
byte[] gasLimit;
byte[] value;
public CallCreate(byte[] data, byte[] destination, byte[] gasLimit, byte[] value) {
this.data = data;
this.destination = destination;
this.gasLimit = gasLimit;
this.value = value;
}
public byte[] getData() {
return data;
}
public byte[] getDestination() {
return destination;
}
public byte[] getGasLimit() {
return gasLimit;
}
public byte[] getValue() {
return value;
}
}

View File

@ -210,12 +210,15 @@ public class Program {
public void createContract(DataWord gas, DataWord memStart, DataWord memSize) {
if (invokeData.byTestingSuite()){
logger.info("[testing suite] - omit real create");
return;
}
// 1. FETCH THE CODE FROM THE MEMORY
ByteBuffer programCode = memoryChunk(memStart, memSize);
Repository trackRepository = result.getRepository().getTrack();
trackRepository.startTracking();
byte[] senderAddress = this.getOwnerAddress().getNoLeadZeroesData();
if (logger.isInfoEnabled())
logger.info("creating a new contract inside contract run: [{}]", Hex.toHexString(senderAddress));
@ -229,14 +232,20 @@ public class Program {
return;
}
// actual gas subtract
this.spendGas(gas.intValue(), "internal call");
// 2.2 CREATE THE CONTRACT ADDRESS
byte[] nonce = trackRepository.getNonce(senderAddress).toByteArray();
byte[] nonce = result.getRepository().getNonce(senderAddress).toByteArray();
byte[] newAddress = HashUtil.calcNewAddr(this.getOwnerAddress().getNoLeadZeroesData(), nonce);
trackRepository.createAccount(newAddress);
result.getRepository().createAccount(newAddress);
// 2.3 UPDATE THE NONCE
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
trackRepository.increaseNonce(senderAddress);
result.getRepository().increaseNonce(senderAddress);
Repository trackRepository = result.getRepository().getTrack();
trackRepository.startTracking();
// 3. COOK THE INVOKE AND EXECUTE
ProgramInvoke programInvoke =
@ -264,6 +273,16 @@ public class Program {
// IN SUCCESS PUSH THE ADDRESS INTO THE STACK
stackPush(new DataWord(newAddress));
trackRepository.commit();
// 5. REFUND THE REMAIN GAS
BigInteger refundGas = gas.value().subtract(BigInteger.valueOf(result.getGasUsed()));
if (refundGas.compareTo(BigInteger.ZERO) == 1){
this.refundGas(refundGas.intValue(), "remain gas from the internal call");
logger.info("The remain gas refunded, account: [ {} ], gas: [ {} ] ",
refundGas.toString(), refundGas.toString());
}
}
/**
@ -288,81 +307,108 @@ public class Program {
// FETCH THE CODE
byte[] programCode = this.result.getRepository().getCode(toAddress);
if (logger.isInfoEnabled())
logger.info("calling for existing contract: address={}",
Hex.toHexString(toAddress));
byte[] senderAddress = this.getOwnerAddress().getNoLeadZeroesData();
// 2.1 PERFORM THE GAS VALUE TX
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
if (this.getGas().longValue() - gas.longValue() < 0 ) {
logger.info("No gas for the internal call, \n" +
"fromAddress={}, toAddress={}",
Hex.toHexString(senderAddress), Hex.toHexString(toAddress));
this.stackPushZero();
return;
}
// actual gas subtract
this.spendGas(gas.intValue(), "internal call");
BigInteger endowment = endowmentValue.value();
BigInteger senderBalance = result.getRepository().getBalance(senderAddress);
if (senderBalance.compareTo(endowment) < 0){
stackPushZero();
return;
}
result.getRepository().addBalance(senderAddress, endowment.negate());
if (invokeData.byTestingSuite()) {
logger.info("[testing suite] - omit real call");
stackPushOne();
this.getResult().addCallCreate(data.array(),
toAddressDW.getNoLeadZeroesData(),
gas.getNoLeadZeroesData(), endowmentValue.getNoLeadZeroesData());
return;
}
Repository trackRepository = result.getRepository().getTrack();
trackRepository.startTracking();
trackRepository.addBalance(toAddress, endowmentValue.value());
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(this, toAddressDW,
endowmentValue, gas, result.getRepository().getBalance(toAddress),
data.array(),
trackRepository);
ProgramResult result = null;
if (programCode != null && programCode.length != 0) {
if (logger.isInfoEnabled())
logger.info("calling for existing contract: address={}",
Hex.toHexString(toAddress));
byte[] senderAddress = this.getOwnerAddress().getNoLeadZeroesData();
// 2.1 PERFORM THE GAS VALUE TX
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
if (this.getGas().longValue() - gas.longValue() < 0 ) {
logger.info("No gas for the internal call, \n" +
"fromAddress={}, toAddress={}",
Hex.toHexString(senderAddress), Hex.toHexString(toAddress));
this.stackPushZero();
return;
}
// 2.2 UPDATE THE NONCE
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
this.result.repository.increaseNonce(senderAddress);
Repository trackRepository = result.getRepository().getTrack();
trackRepository.startTracking();
// todo: check if the endowment can really be done
trackRepository.addBalance(toAddress, endowmentValue.value());
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(this, toAddressDW,
endowmentValue, gas, result.getRepository().getBalance(toAddress),
data.array(),
trackRepository);
VM vm = new VM();
Program program = new Program(programCode, programInvoke);
vm.play(program);
ProgramResult result = program.getResult();
if (result.getException() != null &&
result.getException() instanceof Program.OutOfGasException) {
logger.info("contract run halted by OutOfGas: contract={}" , Hex.toHexString(toAddress));
trackRepository.rollback();
stackPushZero();
return;
}
// 3. APPLY RESULTS: result.getHReturn() into out_memory allocated
ByteBuffer buffer = result.getHReturn();
if (buffer != null) {
int retSize = buffer.array().length;
int allocSize = outDataSize.intValue();
if (retSize > allocSize) {
byte[] outArray = Arrays.copyOf(buffer.array(), allocSize);
this.memorySave(outArray, buffer.array());
} else {
this.memorySave(outDataOffs.getData(), buffer.array());
}
}
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
stackPushOne();
trackRepository.commit();
stackPush(new DataWord(1));
// the gas spent in any internal outcome
// even if execution was halted by an exception
spendGas(result.getGasUsed(), " 'Total for CALL run' ");
logger.info("The usage of the gas in external call updated", result.getGasUsed());
result = program.getResult();
}
if (result.getException() != null &&
result.getException() instanceof Program.OutOfGasException) {
logger.info("contract run halted by OutOfGas: contract={}" , Hex.toHexString(toAddress));
trackRepository.rollback();
stackPushZero();
return;
}
// 3. APPLY RESULTS: result.getHReturn() into out_memory allocated
ByteBuffer buffer = result.getHReturn();
if (buffer != null) {
int retSize = buffer.array().length;
int allocSize = outDataSize.intValue();
if (retSize > allocSize) {
byte[] outArray = Arrays.copyOf(buffer.array(), allocSize);
this.memorySave(outArray, buffer.array());
} else {
this.memorySave(outDataOffs.getData(), buffer.array());
}
}
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
trackRepository.commit();
stackPushOne();
// 5. REFUND THE REMAIN GAS
BigInteger refundGas = gas.value().subtract(BigInteger.valueOf(result.getGasUsed()));
if (refundGas.compareTo(BigInteger.ZERO) == 1){
this.refundGas(refundGas.intValue(), "remain gas from the internal call");
logger.info("The remain gas refunded, account: [ {} ], gas: [ {} ] ",
refundGas.toString(), refundGas.toString());
}
}
public void spendGas(int gasValue, String cause) {
gasLogger.info("Spent: for cause={} gas={}", cause, gasValue);
gasLogger.info("Spent for cause: [ {} ], gas: [ {} ]", cause, gasValue);
long afterSpend = invokeData.getGas().longValue() - gasValue - result.getGasUsed();
if (afterSpend < 0)
@ -370,6 +416,11 @@ public class Program {
result.spendGas(gasValue);
}
public void refundGas(int gasValue, String cause) {
gasLogger.info("Refund for cause: [ {} ], gas: [ {} ]", cause, gasValue);
result.refundGas(gasValue);
}
public void storageSave(DataWord word1, DataWord word2) {
storageSave(word1.getData(), word2.getData());
}

View File

@ -30,4 +30,5 @@ public interface ProgramInvoke {
public Repository getRepository();
public boolean byTransaction();
boolean byTestingSuite();
}

View File

@ -35,6 +35,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
private Repository repository;
private boolean byTransaction = true;
private boolean byTestingSuite = false;
public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance,
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
@ -63,6 +64,17 @@ public class ProgramInvokeImpl implements ProgramInvoke {
this.byTransaction = false;
}
public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] balance,
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
long gaslimit,
Repository repository, boolean byTestingSuite) {
this(address, origin, caller, balance, gasPrice, gas, callValue, msgData, lastHash, coinbase,
timestamp, number, difficulty, gaslimit, repository);
this.byTestingSuite = byTestingSuite;
}
public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] balance,
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
@ -214,6 +226,11 @@ public class ProgramInvokeImpl implements ProgramInvoke {
return byTransaction;
}
@Override
public boolean byTestingSuite() {
return byTestingSuite;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -221,6 +238,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
ProgramInvokeImpl that = (ProgramInvokeImpl) o;
if (byTestingSuite != that.byTestingSuite) return false;
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;
@ -259,8 +277,9 @@ public class ProgramInvokeImpl implements ProgramInvoke {
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 + (repository != null ? repository.hashCode() : 0);
result = 31 * result + (byTransaction ? 1 : 0);
result = 31 * result + (byTestingSuite ? 1 : 0);
return result;
}
}

View File

@ -14,6 +14,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
private byte[] msgData;
private Repository repository = null;
private String ownerAddress = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
@ -27,6 +28,11 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
this.repository.createAccount(Hex.decode(ownerAddress));
}
public ProgramInvokeMockImpl(boolean defaults){
}
/* ADDRESS op */
public DataWord getOwnerAddress() {
byte[] addr = Hex.decode(ownerAddress);
@ -167,6 +173,11 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
return true;
}
@Override
public boolean byTestingSuite() {
return false;
}
@Override
public Repository getRepository() {
return this.repository;
@ -175,4 +186,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
public void setRepository(Repository repository) {
this.repository = repository;
}
}

View File

@ -3,6 +3,8 @@ package org.ethereum.vm;
import org.ethereum.db.Repository;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* www.ethereumJ.com
@ -17,9 +19,19 @@ public class ProgramResult {
Repository repository = null;
/*
* for testing runs ,
* call/create is not executed
* but dummy recorded
*/
List<CallCreate> callCreateList;
public void spendGas(int gas) {
gasUsed += gas;
}
public void refundGas(int gas) {
gasUsed -= gas;
}
public void setHReturn(byte[] hReturn) {
this.hReturn = ByteBuffer.allocate(hReturn.length);
@ -49,4 +61,16 @@ public class ProgramResult {
public void setRepository(Repository repository) {
this.repository = repository;
}
public List<CallCreate> getCallCreateList() {
return callCreateList;
}
public void addCallCreate(byte[] data, byte[] destination, byte[] gasLimit, byte[] value){
if (callCreateList == null)
callCreateList = new ArrayList<>();
callCreateList.add(new CallCreate(data, destination, gasLimit, value));
}
}

View File

@ -556,6 +556,8 @@ public class VM {
program.fullTrace();
} catch (RuntimeException e) {
program.stop();
logger.info("VM halted: ", e);
throw e;
}
}

View File

@ -0,0 +1,175 @@
package org.ethereum.jsontestsuite;
import org.ethereum.db.ByteArrayWrapper;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.junit.Assert;
import org.junit.Test;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 29/06/2014 10:46
*/
public class JSONTestSuiteTest {
@Test // AccountState parsing //
public void test1() throws ParseException {
String expectedNonce = "01";
String expectedBalance = "0de0b6b3a763ff6c";
String expectedCode = "6000600060006000604a3360c85c03f1";
ByteArrayWrapper expectedKey1 = new ByteArrayWrapper( Hex.decode("ffaa") );
ByteArrayWrapper expectedKey2 = new ByteArrayWrapper( Hex.decode("ffab") );
ByteArrayWrapper expectedVal1 = new ByteArrayWrapper( Hex.decode("c8") );
ByteArrayWrapper expectedVal2 = new ByteArrayWrapper( Hex.decode("b2b2b2") );
JSONParser parser = new JSONParser();
String accountString = "{'balance':999999999999999852,'nonce':1," +
"'code':[96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241]," +
"'storage':{'0xffaa' : [200], '0xffab' : ['0xb2b2b2']}}";
accountString = accountString.replace("'", "\"");
JSONObject accountJSONObj = (JSONObject)parser.parse(accountString);
AccountState state =
new AccountState(Hex.decode("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"),
accountJSONObj);
Assert.assertEquals(expectedNonce, Hex.toHexString(state.nonce));
Assert.assertEquals(expectedBalance, Hex.toHexString(state.balance));
Assert.assertEquals(expectedCode, Hex.toHexString(state.code));
Assert.assertTrue( state.storage.keySet().contains(expectedKey1) );
Assert.assertTrue(state.storage.keySet().contains(expectedKey2));
Assert.assertTrue( state.storage.values().contains(expectedVal1) );
Assert.assertTrue(state.storage.values().contains(expectedVal2));
}
@Test // Exec parsing //
public void test2() throws ParseException {
String expectedAddress = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6";
String expectedCaller = "cd1722f3947def4cf144679da39c4c32bdc35681";
String expectedData = "ffaabb";
String expectedCode = "6000600060006000604a3360c85c03f1";
String expectedGas = "2710";
String expectedGasPrice = "5af3107a4000";
String expectedOrigin = "cd1722f3947def4cf144679da39c4c32bdc35681";
String expectedValue = "0de0b6b3a7640000";
JSONParser parser = new JSONParser();
String execString = "{'address' : '0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6'," +
" 'caller' : 'cd1722f3947def4cf144679da39c4c32bdc35681'," +
" 'data' : ['0xffaabb']," +
" 'code' : [96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241]," +
" 'gas' : 10000," +
" 'gasPrice' : 100000000000000," +
" 'origin' : 'cd1722f3947def4cf144679da39c4c32bdc35681'," +
" 'value' : 1000000000000000000}";
execString = execString.replace("'", "\"");
JSONObject execJSONObj = (JSONObject)parser.parse(execString);
Exec exec = new Exec(execJSONObj);
Assert.assertEquals(expectedAddress, Hex.toHexString(exec.getAddress()));
Assert.assertEquals(expectedCaller, Hex.toHexString(exec.getCaller()));
Assert.assertEquals(expectedData, Hex.toHexString(exec.getData()));
Assert.assertEquals(expectedCode, Hex.toHexString(exec.getCode()));
Assert.assertEquals(expectedGas, Hex.toHexString(exec.getGas()));
Assert.assertEquals(expectedGasPrice, Hex.toHexString(exec.getGasPrice()));
Assert.assertEquals(expectedOrigin, Hex.toHexString(exec.getOrigin()));
Assert.assertEquals(expectedValue, Hex.toHexString(exec.getValue()));
}
@Test // Env parsing //
public void test3() throws ParseException {
String expectedCurrentCoinbase = "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba";
String expectedCurrentDifficulty = "256";
String expectedCurrentGasLimit = "1000000";
String expectedCurrentNumber = "0";
String expectedCurrentTimestamp = "1";
String expectedPreviousHash = "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6";
JSONParser parser = new JSONParser();
String envString = "{'currentCoinbase' : '2adc25665018aa1fe0e6bc666dac8fc2697ff9ba'," +
"'currentDifficulty' : '256'," +
"'currentGasLimit' : '1000000'," +
"'currentNumber' : '0'," +
"'currentTimestamp' : 1," +
"'previousHash' : '5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6'}";
envString = envString.replace("'", "\"");
JSONObject envJSONObj = (JSONObject)parser.parse(envString);
Env env = new Env(envJSONObj);
Assert.assertEquals(expectedCurrentCoinbase, Hex.toHexString(env.getCurrentCoinbase()));
Assert.assertEquals(expectedCurrentDifficulty, new BigInteger( env.getCurrentDifficlty()).toString());
Assert.assertEquals(expectedCurrentGasLimit, new BigInteger( env.getCurrentGasLimit()).toString());
Assert.assertEquals(expectedCurrentNumber, new BigInteger( env.getCurrentNumber()).toString());
Assert.assertEquals(expectedCurrentTimestamp, new BigInteger( env.getCurrentTimestamp()).toString());
Assert.assertEquals(expectedPreviousHash, Hex.toHexString(env.getPreviousHash()));
}
@Test // CallCreate parsing //
public void test4() throws ParseException {
String expectedData = "";
String expectedDestination = "cd1722f3947def4cf144679da39c4c32bdc35681";
String expectedGasLimit = "9792";
String expectedValue = "74";
JSONParser parser = new JSONParser();
String callCreateString = "{'data' : [],'destination' : 'cd1722f3947def4cf144679da39c4c32bdc35681','gasLimit' : 9792,'value' : 74}";
callCreateString = callCreateString.replace("'", "\"");
JSONObject callCreateJSONObj = (JSONObject)parser.parse(callCreateString);
CallCreate callCreate = new CallCreate(callCreateJSONObj);
Assert.assertEquals(expectedData, Hex.toHexString(callCreate.getData()));
Assert.assertEquals(expectedDestination, Hex.toHexString(callCreate.getDestination()));
Assert.assertEquals(expectedGasLimit, new BigInteger( callCreate.getGasLimit()).toString());
Assert.assertEquals(expectedValue, new BigInteger( callCreate.getValue()).toString());
}
@Test // TestCase parsing //
public void test5() throws ParseException {
JSONParser parser = new JSONParser();
// String testCaseString = "{'callcreates':[{'data':[],'destination':'cd1722f3947def4cf144679da39c4c32bdc35681','gasLimit':9792,'value':74}],'env':{'currentCoinbase':'2adc25665018aa1fe0e6bc666dac8fc2697ff9ba','currentDifficulty':'256','currentGasLimit':'1000000','currentNumber':'0','currentTimestamp':1,'previousHash':'5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6'},'exec':{'address':'0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6','caller':'cd1722f3947def4cf144679da39c4c32bdc35681','code':[96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241],'data':[],'gas':10000,'gasPrice':100000000000000,'origin':'cd1722f3947def4cf144679da39c4c32bdc35681','value':1000000000000000000},'gas':9971,'out':[],'post':{'0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6':{'balance':999999999999999926,'code':[96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241],'nonce':0,'storage':{}}},'pre':{'0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6':{'balance':1000000000000000000,'code':[96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241],'nonce':0,'storage':{}}}}";
String testCaseString = "{'callcreates':[{'data':[],'destination':'cd1722f3947def4cf144679da39c4c32bdc35681','gasLimit':9792,'value':74}],'env':{'currentCoinbase':'2adc25665018aa1fe0e6bc666dac8fc2697ff9ba','currentDifficulty':'256','currentGasLimit':'1000000','currentNumber':'0','currentTimestamp':1,'previousHash':'5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6'},'exec':{'address':'0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6','caller':'cd1722f3947def4cf144679da39c4c32bdc35681','code':[96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241],'data':[],'gas':10000,'gasPrice':100000000000000,'origin':'cd1722f3947def4cf144679da39c4c32bdc35681','value':1000000000000000000},'gas':9971,'out':[],'post':{'0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6':{'balance':999999999999999926,'code':[96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241],'nonce':0,'storage':{}}},'pre':{'0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6':{'balance':1000000000000000000,'code':[96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241],'nonce':0,'storage':{}}}}";
testCaseString = testCaseString.replace("'", "\"");
JSONObject testCaseJSONObj = (JSONObject)parser.parse(testCaseString);
TestCase testCase = new TestCase(testCaseJSONObj);
int ccList = testCase.getCallCreateList().size();
Assert.assertEquals(1, ccList);
TestRunner runner = new TestRunner();
runner.runTestCase(testCase);
}
}