vmIOandFlowOperationsTest

This commit is contained in:
alon muroch 2014-12-02 11:36:37 +01:00
parent 7c0a6fb65d
commit da9fc3d391
7 changed files with 292 additions and 222 deletions

View File

@ -55,13 +55,21 @@ public class TestCase {
JSONObject envJSON = (JSONObject)testCaseJSONObj.get("env"); JSONObject envJSON = (JSONObject)testCaseJSONObj.get("env");
JSONObject execJSON = (JSONObject)testCaseJSONObj.get("exec"); JSONObject execJSON = (JSONObject)testCaseJSONObj.get("exec");
JSONObject preJSON = (JSONObject)testCaseJSONObj.get("pre"); JSONObject preJSON = (JSONObject)testCaseJSONObj.get("pre");
JSONObject postJSON = (JSONObject)testCaseJSONObj.get("post"); JSONObject postJSON = new JSONObject();
JSONArray callCreates = (JSONArray)testCaseJSONObj.get("callcreates"); if(testCaseJSONObj.containsKey("post")) // in cases where there is no post dictionary (when testing for exceptions for example)
postJSON = (JSONObject)testCaseJSONObj.get("post");
JSONArray callCreates = new JSONArray();
if(testCaseJSONObj.containsKey("callcreates"))
callCreates = (JSONArray)testCaseJSONObj.get("callcreates");
String gasString = testCaseJSONObj.get("gas").toString(); String gasString = "0";
if(testCaseJSONObj.containsKey("gas"))
gasString = testCaseJSONObj.get("gas").toString();
this.gas = ByteUtil.bigIntegerToBytes(new BigInteger(gasString)); this.gas = ByteUtil.bigIntegerToBytes(new BigInteger(gasString));
String outString = testCaseJSONObj.get("out").toString(); String outString = null;
if(testCaseJSONObj.containsKey("out"))
outString = testCaseJSONObj.get("out").toString();
if (outString != null && outString.length() > 2) if (outString != null && outString.length() > 2)
this.out = Hex.decode(outString.substring(2)); this.out = Hex.decode(outString.substring(2));
else else

View File

@ -99,218 +99,249 @@ public class TestRunner {
/* 4. run VM */ /* 4. run VM */
VM vm = new VM(); VM vm = new VM();
Program program = new Program(exec.getCode(), programInvoke); Program program = new Program(exec.getCode(), programInvoke);
while(!program.isStopped()) boolean vmDidThrowAnEception = false;
vm.step(program); RuntimeException e = null;
try {
while(!program.isStopped())
vm.step(program);
}
catch (RuntimeException ex) {
vmDidThrowAnEception = true;
e = ex;
}
program.saveProgramTraceToFile(testCase.getName()); program.saveProgramTraceToFile(testCase.getName());
this.trace = program.getProgramTrace(); if(testCase.getPost().size() == 0) {
if(vmDidThrowAnEception != true) {
String output =
String.format("VM was expected to throw an exception");
logger.info(output);
results.add(output);
}
else
logger.info("VM did throw an exception: " + e.toString());
}
else {
if(vmDidThrowAnEception) {
String output =
String.format("VM threw an unexpected exception: " + e.toString());
logger.info(output);
results.add(output);
return results;
}
System.out.println("--------- POST --------"); this.trace = program.getProgramTrace();
/* 5. Assert Post values */
for (ByteArrayWrapper key : testCase.getPost().keySet()) {
AccountState accountState = testCase.getPost().get(key); System.out.println("--------- POST --------");
/* 5. Assert Post values */
for (ByteArrayWrapper key : testCase.getPost().keySet()) {
long expectedNonce = accountState.getNonceLong(); AccountState accountState = testCase.getPost().get(key);
BigInteger expectedBalance = accountState.getBigIntegerBalance();
byte[] expectedCode = accountState.getCode();
boolean accountExist = (null != repository.getAccountState(key.getData())); long expectedNonce = accountState.getNonceLong();
if (!accountExist) { BigInteger expectedBalance = accountState.getBigIntegerBalance();
byte[] expectedCode = accountState.getCode();
String output = boolean accountExist = (null != repository.getAccountState(key.getData()));
String.format("The expected account does not exist. key: [ %s ]", if (!accountExist) {
Hex.toHexString(key.getData()));
logger.info(output);
results.add(output);
continue;
}
long actualNonce = repository.getNonce(key.getData()).longValue(); String output =
BigInteger actualBalance = repository.getBalance(key.getData()); String.format("The expected account does not exist. key: [ %s ]",
byte[] actualCode = repository.getCode(key.getData()); Hex.toHexString(key.getData()));
if (actualCode == null) actualCode = "".getBytes(); logger.info(output);
results.add(output);
continue;
}
if (expectedNonce != actualNonce) { long actualNonce = repository.getNonce(key.getData()).longValue();
BigInteger actualBalance = repository.getBalance(key.getData());
byte[] actualCode = repository.getCode(key.getData());
if (actualCode == null) actualCode = "".getBytes();
String output = if (expectedNonce != actualNonce) {
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 nonce result is different. key: [ %s ], expectedNonce: [ %d ] is actualNonce: [ %d ] ",
Hex.toHexString(key.getData()), expectedNonce, actualNonce);
logger.info(output);
results.add(output);
}
String output = if (!expectedBalance.equals(actualBalance)) {
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 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);
}
String output = if (!Arrays.equals(expectedCode, actualCode)) {
String.format("The code result is different. account: [ %s ], expectedCode: [ %s ] is actualCode: [ %s ] ",
Hex.toHexString(key.getData()),
Hex.toHexString(expectedCode),
Hex.toHexString(actualCode));
logger.info(output);
results.add(output);
}
// assert storage String output =
Map<ByteArrayWrapper, ByteArrayWrapper> storage = accountState.getStorage(); String.format("The code result is different. account: [ %s ], expectedCode: [ %s ] is actualCode: [ %s ] ",
for (ByteArrayWrapper storageKey : storage.keySet()) { Hex.toHexString(key.getData()),
Hex.toHexString(expectedCode),
Hex.toHexString(actualCode));
logger.info(output);
results.add(output);
}
byte[] expectedStValue = storage.get(storageKey).getData(); // assert storage
Map<ByteArrayWrapper, ByteArrayWrapper> storage = accountState.getStorage();
for (ByteArrayWrapper storageKey : storage.keySet()) {
ContractDetails contractDetails = byte[] expectedStValue = storage.get(storageKey).getData();
program.getResult().getRepository().getContractDetails(accountState.getAddress());
if (contractDetails == null) { ContractDetails contractDetails =
program.getResult().getRepository().getContractDetails(accountState.getAddress());
String output = if (contractDetails == null) {
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(); String output =
DataWord actualValue = testStorage.get(new DataWord(storageKey.getData())); 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;
}
if (actualValue == null || Map<DataWord, DataWord> testStorage = contractDetails.getStorage();
!Arrays.equals(expectedStValue, actualValue.getNoLeadZeroesData())) { DataWord actualValue = testStorage.get(new DataWord(storageKey.getData()));
String output = if (actualValue == null ||
String.format("Storage value different: key [ %s ], expectedValue: [ %s ], actualValue: [ %s ]", !Arrays.equals(expectedStValue, actualValue.getNoLeadZeroesData())) {
Hex.toHexString(storageKey.getData()),
Hex.toHexString(expectedStValue),
actualValue == null ? "" : Hex.toHexString(actualValue.getNoLeadZeroesData()));
logger.info(output);
results.add(output);
}
}
}
// TODO: assert that you have no extra accounts in the repository String output =
// TODO: -> basically the deleted by suicide should be deleted String.format("Storage value different: key [ %s ], expectedValue: [ %s ], actualValue: [ %s ]",
// TODO: -> and no unexpected created Hex.toHexString(storageKey.getData()),
Hex.toHexString(expectedStValue),
actualValue == null ? "" : Hex.toHexString(actualValue.getNoLeadZeroesData()));
logger.info(output);
results.add(output);
}
}
}
List<org.ethereum.vm.CallCreate> resultCallCreates = // TODO: assert that you have no extra accounts in the repository
program.getResult().getCallCreateList(); // TODO: -> basically the deleted by suicide should be deleted
// TODO: -> and no unexpected created
// assert call creates List<org.ethereum.vm.CallCreate> resultCallCreates =
for (int i = 0; i < testCase.getCallCreateList().size(); ++i) { program.getResult().getCallCreateList();
org.ethereum.vm.CallCreate resultCallCreate = null; // assert call creates
if (resultCallCreates != null && resultCallCreates.size() > i) { for (int i = 0; i < testCase.getCallCreateList().size(); ++i) {
resultCallCreate = resultCallCreates.get(i);
}
CallCreate expectedCallCreate = testCase.getCallCreateList().get(i); org.ethereum.vm.CallCreate resultCallCreate = null;
if (resultCallCreates != null && resultCallCreates.size() > i) {
resultCallCreate = resultCallCreates.get(i);
}
if (resultCallCreate == null && expectedCallCreate != null) { CallCreate expectedCallCreate = testCase.getCallCreateList().get(i);
String output = if (resultCallCreate == null && expectedCallCreate != null) {
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; 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);
boolean assertDestination = Arrays.equals( continue;
expectedCallCreate.getDestination(), }
resultCallCreate.getDestination());
if (!assertDestination) {
String output = boolean assertDestination = Arrays.equals(
String.format("Call/Create destination is different. Expected: [ %s ], result: [ %s ]", expectedCallCreate.getDestination(),
Hex.toHexString(expectedCallCreate.getDestination()), resultCallCreate.getDestination());
Hex.toHexString(resultCallCreate.getDestination())); if (!assertDestination) {
logger.info(output);
results.add(output);
}
boolean assertData = Arrays.equals( String output =
expectedCallCreate.getData(), String.format("Call/Create destination is different. Expected: [ %s ], result: [ %s ]",
resultCallCreate.getData()); Hex.toHexString(expectedCallCreate.getDestination()),
if (!assertData) { Hex.toHexString(resultCallCreate.getDestination()));
logger.info(output);
results.add(output);
}
String output = boolean assertData = Arrays.equals(
String.format("Call/Create data is different. Expected: [ %s ], result: [ %s ]", expectedCallCreate.getData(),
Hex.toHexString(expectedCallCreate.getData()), resultCallCreate.getData());
Hex.toHexString(resultCallCreate.getData())); if (!assertData) {
logger.info(output);
results.add(output);
}
boolean assertGasLimit = Arrays.equals( String output =
expectedCallCreate.getGasLimit(), String.format("Call/Create data is different. Expected: [ %s ], result: [ %s ]",
resultCallCreate.getGasLimit()); Hex.toHexString(expectedCallCreate.getData()),
if (!assertGasLimit) { Hex.toHexString(resultCallCreate.getData()));
String output = logger.info(output);
String.format("Call/Create gasLimit is different. Expected: [ %s ], result: [ %s ]", results.add(output);
Hex.toHexString(expectedCallCreate.getGasLimit()), }
Hex.toHexString(resultCallCreate.getGasLimit()));
logger.info(output);
results.add(output);
}
boolean assertValue = Arrays.equals( boolean assertGasLimit = Arrays.equals(
expectedCallCreate.getValue(), expectedCallCreate.getGasLimit(),
resultCallCreate.getValue()); resultCallCreate.getGasLimit());
if (!assertValue) { if (!assertGasLimit) {
String output = String output =
String.format("Call/Create value is different. Expected: [ %s ], result: [ %s ]", String.format("Call/Create gasLimit is different. Expected: [ %s ], result: [ %s ]",
Hex.toHexString(expectedCallCreate.getValue()), Hex.toHexString(expectedCallCreate.getGasLimit()),
Hex.toHexString(resultCallCreate.getValue())); Hex.toHexString(resultCallCreate.getGasLimit()));
logger.info(output); logger.info(output);
results.add(output); results.add(output);
} }
}
// assert out boolean assertValue = Arrays.equals(
byte[] expectedHReturn = testCase.getOut(); expectedCallCreate.getValue(),
byte[] actualHReturn = ByteUtil.EMPTY_BYTE_ARRAY; resultCallCreate.getValue());
if (program.getResult().getHReturn() != null) { if (!assertValue) {
actualHReturn = program.getResult().getHReturn().array(); 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);
}
}
if (!Arrays.equals(expectedHReturn, actualHReturn)) { // assert out
byte[] expectedHReturn = testCase.getOut();
byte[] actualHReturn = ByteUtil.EMPTY_BYTE_ARRAY;
if (program.getResult().getHReturn() != null) {
actualHReturn = program.getResult().getHReturn().array();
}
String output = if (!Arrays.equals(expectedHReturn, actualHReturn)) {
String.format("HReturn is different. Expected hReturn: [ %s ], actual hReturn: [ %s ]",
Hex.toHexString(expectedHReturn),
Hex.toHexString(actualHReturn));
logger.info(output);
results.add(output);
}
// assert gas String output =
BigInteger expectedGas = new BigInteger(testCase.getGas()); String.format("HReturn is different. Expected hReturn: [ %s ], actual hReturn: [ %s ]",
BigInteger actualGas = new BigInteger(gas).subtract(BigInteger.valueOf(program.getResult().getGasUsed())); Hex.toHexString(expectedHReturn),
Hex.toHexString(actualHReturn));
logger.info(output);
results.add(output);
}
if (!expectedGas.equals(actualGas)) { // 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("Gas remaining is different. Expected gas remaining: [ %s ], actual gas remaining: [ %s ]",
expectedGas.toString() ,
actualGas.toString());
logger.info(output);
results.add(output);
}
/*
* end of if(testCase.getPost().size() == 0)
*/
}
String output =
String.format("Gas remaining is different. Expected gas remaining: [ %s ], actual gas remaining: [ %s ]",
expectedGas.toString() ,
actualGas.toString());
logger.info(output);
results.add(output);
}
return results; return results;
} finally { } finally {
repository.close(); repository.close();

View File

@ -21,6 +21,7 @@ public class TestSuite {
for (Object key: testCaseJSONObj.keySet()){ for (Object key: testCaseJSONObj.keySet()){
Object testCaseJSON = testCaseJSONObj.get(key); Object testCaseJSON = testCaseJSONObj.get(key);
TestCase testCase = new TestCase(key.toString(), (JSONObject) testCaseJSON); TestCase testCase = new TestCase(key.toString(), (JSONObject) testCaseJSON);
testList.add(testCase); testList.add(testCase);

View File

@ -78,6 +78,8 @@ public class DataWord implements Comparable<DataWord> {
*/ */
public int intValue() { public int intValue() {
BigDecimal tmpValue = new BigDecimal(this.value()); BigDecimal tmpValue = new BigDecimal(this.value());
if(this.bytesOccupied() > 4)
return Integer.MAX_VALUE;
return tmpValue.intValueExact(); return tmpValue.intValueExact();
} }

View File

@ -47,6 +47,7 @@ public class Program {
byte[] ops; byte[] ops;
int pc = 0; int pc = 0;
byte lastOp = 0; byte lastOp = 0;
byte previouslyExecutedOp = 0;
boolean stopped = false; boolean stopped = false;
ProgramInvoke invokeData; ProgramInvoke invokeData;
@ -76,10 +77,30 @@ public class Program {
return ops[pc]; return ops[pc];
} }
/**
* Last Op can only be set publicly (no getLastOp method), is used for logging
* @param op
*/
public void setLastOp(byte op) { public void setLastOp(byte op) {
this.lastOp = op; this.lastOp = op;
} }
/**
* Should be set only after the OP is fully executed
* @param op
*/
public void setPreviouslyExecutedOp(byte op) {
this.previouslyExecutedOp = op;
}
/**
* returns the last fully executed OP
* @return
*/
public byte getPreviouslyExecutedOp() {
return this.previouslyExecutedOp;
}
public void stackPush(byte[] data) { public void stackPush(byte[] data) {
DataWord stackWord = new DataWord(data); DataWord stackWord = new DataWord(data);
stack.push(stackWord); stack.push(stackWord);

View File

@ -821,8 +821,10 @@ public class VM {
case JUMP:{ case JUMP:{
DataWord pos = program.stackPop(); DataWord pos = program.stackPop();
int nextPC = pos.intValue(); // possible overflow int nextPC = pos.intValue(); // possible overflow
if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val()) if(program.getPreviouslyExecutedOp() < OpCode.PUSH1.val() || program.getPreviouslyExecutedOp() > OpCode.PUSH32.val()) {
throw program.new BadJumpDestinationException(); if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val())
throw program.new BadJumpDestinationException();
}
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
hint = "~> " + nextPC; hint = "~> " + nextPC;
@ -836,8 +838,10 @@ public class VM {
if (!cond.isZero()) { if (!cond.isZero()) {
int nextPC = pos.intValue(); // possible overflow int nextPC = pos.intValue(); // possible overflow
if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val()) if(program.getPreviouslyExecutedOp() < OpCode.PUSH1.val() || program.getPreviouslyExecutedOp() > OpCode.PUSH32.val()) {
throw program.new BadJumpDestinationException(); if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val())
throw program.new BadJumpDestinationException();
}
// todo: in case destination is not JUMPDEST, check if prev was strict push // todo: in case destination is not JUMPDEST, check if prev was strict push
// todo: in EP: (ii) If a jump is preceded by a push, no jumpdest required; // todo: in EP: (ii) If a jump is preceded by a push, no jumpdest required;
@ -971,6 +975,8 @@ public class VM {
break; break;
} }
program.setPreviouslyExecutedOp(op.val());
if (logger.isInfoEnabled() && !op.equals(CALL) if (logger.isInfoEnabled() && !op.equals(CALL)
&& !op.equals(CREATE)) && !op.equals(CREATE))
logger.info(logString, stepBefore, String.format("%-12s", logger.info(logString, stepBefore, String.format("%-12s",

View File

@ -11,6 +11,7 @@ import org.junit.runners.MethodSorters;
public class GitHubVMTest { public class GitHubVMTest {
@Test // testing full suite @Test // testing full suite
@Ignore
public void testArithmeticFromGitHub() throws ParseException { public void testArithmeticFromGitHub() throws ParseException {
String json = JSONReader.loadJSON("VMTests/vmArithmeticTest.json"); String json = JSONReader.loadJSON("VMTests/vmArithmeticTest.json");
@ -18,6 +19,7 @@ public class GitHubVMTest {
} }
@Test // testing full suite @Test // testing full suite
@Ignore
public void testBitwiseLogicOperationFromGitHub() throws ParseException { public void testBitwiseLogicOperationFromGitHub() throws ParseException {
String json = JSONReader.loadJSON("VMTests/vmBitwiseLogicOperationTest.json"); String json = JSONReader.loadJSON("VMTests/vmBitwiseLogicOperationTest.json");
@ -41,7 +43,6 @@ public class GitHubVMTest {
} }
@Test // testing full suite @Test // testing full suite
@Ignore
public void testIOandFlowOperationsFromGitHub() throws ParseException { public void testIOandFlowOperationsFromGitHub() throws ParseException {
String json = JSONReader.loadJSON("vmtests/vmIOandFlowOperationsTest.json"); String json = JSONReader.loadJSON("vmtests/vmIOandFlowOperationsTest.json");