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.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
|
||||||
|
@ -15,7 +17,7 @@ import java.nio.ByteBuffer;
|
||||||
|
|
||||||
public class DataWord {
|
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];
|
byte[] data = new byte[32];
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(){
|
||||||
|
|
|
@ -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();
|
||||||
|
@ -236,31 +237,71 @@ public class VM {
|
||||||
break;
|
break;
|
||||||
case MLOAD:{
|
case MLOAD:{
|
||||||
DataWord addr = program.stackPull();
|
DataWord addr = program.stackPull();
|
||||||
DataWord data = program.memoryLoad(addr);
|
DataWord data = program.memoryLoad(addr);
|
||||||
program.stackPush(data);
|
program.stackPush(data);
|
||||||
program.step();
|
program.step();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSTORE:{
|
case MSTORE:{
|
||||||
DataWord addr = program.stackPull();
|
DataWord addr = program.stackPull();
|
||||||
DataWord value = program.stackPull();
|
DataWord value = program.stackPull();
|
||||||
|
|
||||||
program.memorySave(addr, value);
|
program.memorySave(addr, value);
|
||||||
program.step();
|
program.step();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSTORE8:
|
case MSTORE8:{
|
||||||
break;
|
|
||||||
case SLOAD:
|
DataWord addr = program.stackPull();
|
||||||
break;
|
DataWord value = program.stackPull();
|
||||||
case SSTORE:
|
byte[] byteVal = {value.getData()[31]};
|
||||||
break;
|
program.memorySave(addr.getData(), byteVal);
|
||||||
case JUMP:
|
program.step();
|
||||||
break;
|
}
|
||||||
case JUMPI:
|
break;
|
||||||
break;
|
case SLOAD:{
|
||||||
case PC:
|
DataWord key = program.stackPull();
|
||||||
break;
|
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:
|
case MEMSIZE:
|
||||||
break;
|
break;
|
||||||
case GAS:
|
case GAS:
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue