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.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static java.util.Arrays.*;
/** /**
* www.ethereumJ.com * www.ethereumJ.com
* User: Roman Mandeleil * User: Roman Mandeleil
@ -23,6 +25,13 @@ public class DataWord {
data = new byte[32]; 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){ public DataWord(byte[] data){
if (data == null || data.length > 32) if (data == null || data.length > 32)
throw new RuntimeException("bad push data: " + data); throw new RuntimeException("bad push data: " + data);
@ -35,10 +44,9 @@ public class DataWord {
} }
public BigInteger value(){ public BigInteger value(){
return new BigInteger(data); return new BigInteger(1, data);
} }
public boolean isZero(){ public boolean isZero(){
byte result = 0; byte result = 0;
@ -99,4 +107,21 @@ public class DataWord {
return new DataWord(Arrays.clone(data)); 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); 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(){ public void step(){
++pc; ++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 keyWord = new DataWord(key);
DataWord valWord = new DataWord(val); DataWord valWord = new DataWord(val);
storage.put(keyWord, valWord); storage.put(keyWord, valWord);
} }
public void storageLoad(){} public DataWord storageLoad(DataWord key){
return storage.get(key);
}
public void fullTrace(){ public void fullTrace(){

View File

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

View File

@ -1245,4 +1245,323 @@ public class VMTest {
fail(); 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();
}
} }