VM impl: NET, LT, GT, EQ, NOT, BYTE ops implemented & unit tested
This commit is contained in:
parent
953acf417f
commit
d8ebe45f38
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue