From 63e217e6304db188621f229042d21b8d111bd26c Mon Sep 17 00:00:00 2001 From: romanman Date: Mon, 2 Jun 2014 14:56:01 +0300 Subject: [PATCH] VM impl: MSTORE8, SSTORE, SLOAD, JUMP, JUMPI, PC ops implemented and unit tested --- .../main/java/org/ethereum/vm/DataWord.java | 31 +- .../main/java/org/ethereum/vm/Program.java | 22 +- .../src/main/java/org/ethereum/vm/VM.java | 105 ++++-- .../src/test/java/org/ethereum/vm/VMTest.java | 319 ++++++++++++++++++ 4 files changed, 440 insertions(+), 37 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java b/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java index 365b0401..3fa57705 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/DataWord.java @@ -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); + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java index a9dcc60f..648538c0 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -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(){ diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java index 3e0a2a9b..e3718632 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -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: diff --git a/ethereumj-core/src/test/java/org/ethereum/vm/VMTest.java b/ethereumj-core/src/test/java/org/ethereum/vm/VMTest.java index f9b3cc2a..28e55f43 100644 --- a/ethereumj-core/src/test/java/org/ethereum/vm/VMTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/vm/VMTest.java @@ -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(); + } + }