VM impl: NET, LT, GT, EQ, NOT, BYTE ops implemented & unit tested

This commit is contained in:
romanman 2014-06-01 22:43:02 +03:00
parent 953acf417f
commit d8ebe45f38
5 changed files with 516 additions and 65 deletions

View File

@ -0,0 +1,96 @@
package org.ethereum.vm;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.nio.ByteBuffer;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 01/06/2014 19:47
*/
public class DataWord {
static DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack
byte[] data = new byte[32];
public DataWord(){
data = new byte[32];
}
public DataWord(byte[] data){
if (data == null || data.length > 32)
throw new RuntimeException("bad push data: " + data);
System.arraycopy(data, 0, this.data, 32 - data.length, data.length);
}
public byte[] getData() {
return data;
}
public BigInteger value(){
return new BigInteger(data);
}
public boolean isZero(){
byte result = 0;
for (byte tmp : data){
result |= tmp;
}
return result == 0;
}
public String toString(){
return Hex.toHexString(data);
}
public DataWord and(DataWord w2){
for (int i = 0; i < this.data.length; ++i){
this.data[i] &= w2.data[i];
}
return this;
}
public DataWord or(DataWord w2){
for (int i = 0; i < this.data.length; ++i){
this.data[i] |= w2.data[i];
}
return this;
}
public DataWord xor(DataWord w2){
for (int i = 0; i < this.data.length; ++i){
this.data[i] ^= w2.data[i];
}
return this;
}
public void negate(){
if (this.isZero()) return;
for (int i = 0; i < this.data.length; ++i){
this.data[i] = (byte) ~this.data[i];
}
for (int i = this.data.length - 1; i >= 0 ; --i){
this.data[i] = (byte) (1 + this.data[i] & 0xFF);
if (this.data[i] != 0) break;
}
}
}

View File

@ -22,9 +22,9 @@ public enum OpCode {
EXP(0x08),
NEG(0x09),
LT(0X0A),
SLT(0X0B),
SGT(0X0C),
GT(0X0D),
GT(0X0B),
SLT(0X0C),
SGT(0X0D),
EQ(0X0E),
NOT(0X0F),

View File

@ -65,7 +65,7 @@ public class Program {
public DataWord stackPull(){
if (stack.size() == 0) throw new RuntimeException("required pull for empty stack");
if (stack.size() == 0) throw new RuntimeException("attempted pull action for empty stack");
return stack.pop();
};
@ -161,50 +161,4 @@ public class Program {
};
}
class DataWord {
byte[] data = new byte[32];
public DataWord(byte[] data){
if (data == null || data.length > 32)
throw new RuntimeException("bad push data: " + data);
System.arraycopy(data, 0, this.data, 32 - data.length, data.length);
}
public byte[] getData() {
return data;
}
public String toString(){
return Hex.toHexString(data);
}
public DataWord and(DataWord w2){
for (int i = 0; i < this.data.length; ++i){
this.data[i] &= w2.data[i];
}
return this;
}
public DataWord or(DataWord w2){
for (int i = 0; i < this.data.length; ++i){
this.data[i] |= w2.data[i];
}
return this;
}
public DataWord xor(DataWord w2){
for (int i = 0; i < this.data.length; ++i){
this.data[i] ^= w2.data[i];
}
return this;
}
}
}

View File

@ -2,8 +2,8 @@ package org.ethereum.vm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.ethereum.vm.Program.DataWord;
import javax.xml.crypto.Data;
import java.math.BigInteger;
import static org.ethereum.vm.OpCode.PUSH1;
@ -16,6 +16,8 @@ import static org.ethereum.vm.OpCode.PUSH1;
public class VM {
static private BigInteger _32_ = BigInteger.valueOf(32);
Logger logger = LoggerFactory.getLogger("VM");
@ -23,6 +25,7 @@ public class VM {
byte op = program.getCurrentOp();
logger.debug("Op: {}" ,OpCode.code(op).name());
switch (OpCode.code(op)){
@ -49,20 +52,64 @@ public class VM {
// break;
// case EXP:
// break;
// case NEG:
// break;
// case LT:
// break;
case NEG:{
DataWord word1 = program.stackPull();
word1.negate();
program.stackPush(word1);
program.step();
}break;
case LT:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
if (word1.value().compareTo(word2.value()) == -1){
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
} else {
word1.and(DataWord.ZERO);
}
program.stackPush(word1);
program.step();
}break;
// case SLT:
// break;
// case SGT:
// break;
// case GT:
// break;
// case EQ:
// break;
// case NOT:
// break;
case GT:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
if (word1.value().compareTo(word2.value()) == 1){
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
} else {
word1.and(DataWord.ZERO);
}
program.stackPush(word1);
program.step();
}break;
case EQ:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
if (word1.xor(word2).isZero()){
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
} else {
word1.and(DataWord.ZERO);
}
program.stackPush(word1);
program.step();
}
break;
case NOT: {
DataWord word1 = program.stackPull();
if (word1.isZero()){
word1.getData()[31] = 1;
} else {
word1.and(DataWord.ZERO);
}
program.stackPush(word1);
program.step();
}
break;
@ -75,6 +122,7 @@ public class VM {
DataWord word2 = program.stackPull();
word1.and(word2);
program.stackPush(word1);
program.step();
}
break;
case OR: {
@ -82,6 +130,7 @@ public class VM {
DataWord word2 = program.stackPull();
word1.or(word2);
program.stackPush(word1);
program.step();
}
break;
case XOR: {
@ -89,10 +138,27 @@ public class VM {
DataWord word2 = program.stackPull();
word1.xor(word2);
program.stackPush(word1);
program.step();
}
break;
case BYTE:{
DataWord word1 = program.stackPull();
DataWord word2 = program.stackPull();
DataWord result = null;
if (word1.value().compareTo(_32_) == -1){
byte tmp = word2.getData()[word1.value().intValue()];
word2.and(DataWord.ZERO);
word2.getData()[31] = tmp;
result = word2;
} else
result = new DataWord();
program.stackPush(result);
program.step();
}
break;
// case BYTE:
// break;
/**
* SHA3
@ -206,7 +272,6 @@ public class VM {
case PUSH31:
case PUSH32:
logger.debug("Op: {}" ,OpCode.code(op).name());
program.step();
int nPush = op - PUSH1.val() + 1;

View File

@ -475,7 +475,7 @@ public class VMTest {
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase() );
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // AND OP
@ -597,4 +597,340 @@ public class VMTest {
fail();
}
@Test // BYTE OP
public void testBYTE_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("65AABBCCDDEEFF601E13"));
String expected = "00000000000000000000000000000000000000000000000000000000000000EE";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase() );
}
@Test // BYTE OP
public void testBYTE_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("65AABBCCDDEEFF602013"));
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase() );
}
@Test // BYTE OP
public void testBYTE_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("65AABBCCDDEE3A601F13"));
String expected = "000000000000000000000000000000000000000000000000000000000000003A";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase() );
}
@Test // BYTE OP mal data
public void testBYTE_4(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("65AABBCCDDEE3A13"));
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // NOT OP
public void testNOT_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("60000F"));
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase() );
}
@Test // NOT OP
public void testNOT_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("602A0F"));
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase() );
}
@Test // NOT OP mal data
public void testNOT_3(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("0F"));
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // EQ OP
public void testEQ_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("602A602A0E"));
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase() );
}
@Test // EQ OP
public void testEQ_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("622A3B4C622A3B4C0E"));
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase() );
}
@Test // EQ OP
public void testEQ_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("622A3B5C622A3B4C0E"));
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // EQ OP mal data
public void testEQ_4(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("622A3B4C0E"));
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // GT OP
public void testGT_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("600160020B"));
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // GT OP
public void testGT_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("6001610F000B"));
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // GT OP
public void testGT_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("6301020304610F000B"));
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // GT OP mal data
public void testGT_4(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("622A3B4C0B"));
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // LT OP
public void testLT_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("600160020A"));
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // LT OP
public void testLT_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("6001610F000A"));
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // LT OP
public void testLT_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("6301020304610F000A"));
String expected = "0000000000000000000000000000000000000000000000000000000000000001";
vm.step(program);
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // LT OP mal data
public void testLT_4(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("622A3B4C0A"));
vm.step(program);
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
@Test // NEG OP
public void testNEG_1(){
VM vm = new VM();
Program program = new Program(Hex.decode("600109"));
String expected = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // NEG OP
public void testNEG_2(){
VM vm = new VM();
Program program = new Program(Hex.decode("61A00309"));
String expected = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5FFD";
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // NEG OP
public void testNEG_3(){
VM vm = new VM();
Program program = new Program(Hex.decode("61000009"));
String expected = "0000000000000000000000000000000000000000000000000000000000000000";
vm.step(program);
vm.step(program);
Assert.assertEquals(expected, Hex.toHexString(program.stack.get(0).data).toUpperCase());
}
@Test // NEG OP
public void testNEG_4(){
try {
VM vm = new VM();
Program program = new Program(Hex.decode("09"));
vm.step(program);
vm.step(program);
} catch (RuntimeException e) {
return;
}
fail();
}
}