VM impl: MSTORE8, SSTORE, SLOAD, JUMP, JUMPI, PC ops implemented and unit tested
This commit is contained in:
parent
b561a7890d
commit
63e217e630
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(){
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue