VM impl: MSTORE8, SSTORE, SLOAD, JUMP, JUMPI, PC ops implemented and unit tested

This commit is contained in:
romanman 2014-06-02 14:56:01 +03:00
parent b561a7890d
commit 63e217e630
4 changed files with 440 additions and 37 deletions

View File

@ -7,6 +7,8 @@ import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import static java.util.Arrays.*;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
@ -15,7 +17,7 @@ import java.nio.ByteBuffer;
public class DataWord {
static DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack
static DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack
byte[] data = new byte[32];
@ -23,6 +25,13 @@ public class DataWord {
data = new byte[32];
}
public DataWord(int num){
ByteBuffer bInt = ByteBuffer.allocate(4).putInt(num);
ByteBuffer data = ByteBuffer.allocate(32);
System.arraycopy(bInt.array(), 0, data.array(), 28, 4);
this.data = data.array();
}
public DataWord(byte[] data){
if (data == null || data.length > 32)
throw new RuntimeException("bad push data: " + data);
@ -35,10 +44,9 @@ public class DataWord {
}
public BigInteger value(){
return new BigInteger(data);
return new BigInteger(1, data);
}
public boolean isZero(){
byte result = 0;
@ -99,4 +107,21 @@ public class DataWord {
return new DataWord(Arrays.clone(data));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DataWord dataWord = (DataWord) o;
if (!java.util.Arrays.equals(data, dataWord.data)) return false;
return true;
}
@Override
public int hashCode() {
return java.util.Arrays.hashCode(data);
}
}

View File

@ -49,6 +49,19 @@ public class Program {
stack.push(stackWord);
}
public int getPC() {
return pc;
}
public void setPC(DataWord pc) {
this.pc = pc.value().intValue();
if (this.pc > ops.length) throw new RuntimeException("pc overflow pc: " + pc);
}
public void setPC(int pc) {
this.pc = pc;
}
public void step(){
++pc;
@ -122,15 +135,20 @@ public class Program {
}
}
public void storageSave(byte[] key, byte[] val){
public void storageSave(DataWord word1, DataWord word2){
storageSave(word1.getData(), word2.getData());
}
public void storageSave(byte[] key, byte[] val){
DataWord keyWord = new DataWord(key);
DataWord valWord = new DataWord(val);
storage.put(keyWord, valWord);
}
public void storageLoad(){}
public DataWord storageLoad(DataWord key){
return storage.get(key);
}
public void fullTrace(){

View File

@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
import javax.xml.crypto.Data;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import static org.ethereum.vm.OpCode.PUSH1;
@ -34,24 +35,24 @@ public class VM {
* Stop and Arithmetic Operations
*/
// case STOP:
// break;
// case ADD:
// break;
// case MUL:
// break;
// case SUB:
// break;
// case DIV:
// break;
// case SDIV:
// break;
// case MOD:
// break;
// case SMOD:
// break;
// case EXP:
// break;
case STOP:
break;
case ADD:
break;
case MUL:
break;
case SUB:
break;
case DIV:
break;
case SDIV:
break;
case MOD:
break;
case SMOD:
break;
case EXP:
break;
case NEG:{
DataWord word1 = program.stackPull();
word1.negate();
@ -236,31 +237,71 @@ public class VM {
break;
case MLOAD:{
DataWord addr = program.stackPull();
DataWord data = program.memoryLoad(addr);
DataWord data = program.memoryLoad(addr);
program.stackPush(data);
program.step();
}
break;
case MSTORE:{
DataWord addr = program.stackPull();
DataWord addr = program.stackPull();
DataWord value = program.stackPull();
program.memorySave(addr, value);
program.step();
}
break;
case MSTORE8:
break;
case SLOAD:
break;
case SSTORE:
break;
case JUMP:
break;
case JUMPI:
break;
case PC:
break;
case MSTORE8:{
DataWord addr = program.stackPull();
DataWord value = program.stackPull();
byte[] byteVal = {value.getData()[31]};
program.memorySave(addr.getData(), byteVal);
program.step();
}
break;
case SLOAD:{
DataWord key = program.stackPull();
DataWord val = program.storageLoad(key);
if (val == null){
val = key.and(DataWord.ZERO);
}
program.stackPush(val);
program.step();
}
break;
case SSTORE:{
DataWord addr = program.stackPull();
DataWord value = program.stackPull();
program.storageSave(addr, value);
program.step();
}
break;
case JUMP:{
DataWord pos = program.stackPull();
program.setPC(pos);
}
break;
case JUMPI:{
DataWord pos = program.stackPull();
DataWord cond = program.stackPull();
if (!cond.isZero()){
program.setPC(pos);
} else{
program.step();
}
}
break;
case PC:{
int pc = program.getPC();
DataWord pcWord = new DataWord(pc);
program.stackPush(pcWord);
program.step();
}
break;
case MEMSIZE:
break;
case GAS:

View File

@ -1245,4 +1245,323 @@ public class VMTest {
fail();
}
@Test // MSTORE8 OP
public void testMSTORE8_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("6011600055"));
String m_expected = "1100000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(m_expected, Hex.toHexString(program.memory.array()));
}
@Test // MSTORE8 OP
public void testMSTORE8_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("6022600155"));
String m_expected = "0022000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(m_expected, Hex.toHexString(program.memory.array()));
}
@Test // MSTORE8 OP
public void testMSTORE8_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("6022602155"));
String m_expected = "0000000000000000000000000000000000000000000000000000000000000000" +
"0022000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(m_expected, Hex.toHexString(program.memory.array()));
}
@Test // MSTORE8 OP mal
public void testMSTORE8_4(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("602255"));
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // SSTORE OP
public void testSSTORE_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("602260AA57"));
String s_expected_key = "00000000000000000000000000000000000000000000000000000000000000AA";
String s_expected_val = "0000000000000000000000000000000000000000000000000000000000000022";
vm.step(program);
vm.step(program);
vm.step(program);
DataWord key = program.storage.keySet().iterator().next();
Assert.assertEquals(s_expected_key,
Hex.toHexString(key.getData()).toUpperCase());
DataWord val = program.storage.get(key);
Assert.assertEquals(s_expected_val,
Hex.toHexString(val.getData()).toUpperCase());
}
@Test // SSTORE OP
public void testSSTORE_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("602260AA57602260BB57"));
String s_expected_key = "00000000000000000000000000000000000000000000000000000000000000BB";
String s_expected_val = "0000000000000000000000000000000000000000000000000000000000000022";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
DataWord key = program.storage.keySet().iterator().next();
Assert.assertEquals(s_expected_key,
Hex.toHexString(key.getData()).toUpperCase());
DataWord val = program.storage.get(key);
Assert.assertEquals(s_expected_val,
Hex.toHexString(val.getData()).toUpperCase());
}
@Test // SSTORE OP
public void testSSTORE_3(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("602257"));
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // SLOAD OP
public void testSLOAD_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("60AA56"));
String s_expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
Assert.assertEquals(s_expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SLOAD OP
public void testSLOAD_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("602260AA5760AA56"));
String s_expected = "0000000000000000000000000000000000000000000000000000000000000022";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(s_expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SLOAD OP
public void testSLOAD_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("602260AA57603360CC5760CC56"));
String s_expected = "0000000000000000000000000000000000000000000000000000000000000033";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(s_expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // SLOAD OP
public void testSLOAD_4(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("56"));
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // PC OP
public void testPC_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("5A"));
String s_expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
Assert.assertEquals(s_expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // PC OP
public void testPC_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("602260AA5760AA565A"));
String s_expected = "0000000000000000000000000000000000000000000000000000000000000008";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(s_expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // JUMP OP
public void testJUMP_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("60AA60BB600C5860CC60DD60EE60FF"));
String s_expected = "00000000000000000000000000000000000000000000000000000000000000FF";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(s_expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // JUMP OP mal data
public void testJUMP_2(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("600C5860CC60DD60EE60FF"));
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // JUMPI OP
public void testJUMPI_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("600160055960CC"));
String s_expected = "00000000000000000000000000000000000000000000000000000000000000CC";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(s_expected, Hex.toHexString(program.stack.peek().data).toUpperCase());
}
@Test // JUMPI OP
public void testJUMPI_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("630000000060445960CC60DD"));
String s_expected_1 = "00000000000000000000000000000000000000000000000000000000000000DD";
String s_expected_2 = "00000000000000000000000000000000000000000000000000000000000000CC";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
DataWord item1 = program.stack.pop();
DataWord item2 = program.stack.pop();
Assert.assertEquals(s_expected_1, Hex.toHexString(item1.data).toUpperCase());
Assert.assertEquals(s_expected_2, Hex.toHexString(item2.data).toUpperCase());
}
@Test // JUMPI OP mal
public void testJUMPI_3(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("600159"));
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // JUMPI OP mal
public void testJUMPI_4(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("6001602259"));
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
}