From 332707df0f9b9a7115fc8bd224b211dcf232f0c4 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sun, 14 Sep 2014 17:12:51 +0200 Subject: [PATCH] Implement ADDMOD and MULMOD with unit tests --- .../main/java/org/ethereum/vm/Program.java | 30 +++- .../src/main/java/org/ethereum/vm/VM.java | 53 ++++-- .../src/test/java/org/ethereum/vm/VMTest.java | 152 ++++++++++++++++-- 3 files changed, 199 insertions(+), 36 deletions(-) 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 4568d863..74ee142e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -340,22 +340,44 @@ public class Program { } } } - + /** - * That method implement internal calls - * and code invocations + * That method implement internal code invocations. + * + * Instead of immediately calling it adds the call + * to a post-queue, to be executed after everything else + * (including prior-created posts) within the scope + * of that transaction execution is executed. * * @param gas - gas to pay for the call, remaining gas will be refunded to the caller * @param toAddressDW - address to call + * @param storageAddressDW - address of the storage to use + * @param endowmentValue - the value that can be transfer along with the code execution + * @param inDataOffs - start of memory to be input data to the call + * @param inDataSize - size of memory to be input data to the call + */ + public void postToAddress(DataWord gas, DataWord toAddressDW, DataWord storageAddressDW, + DataWord endowmentValue, DataWord inDataOffs, DataWord inDataSize) { + // TODO implement code + } + + /** + * That method implement internal code invocations + * + * @param gas - gas to pay for the call, remaining gas will be refunded to the caller + * @param toAddressDW - address to call + * @param storageAddressDW - address of the storage to use * @param endowmentValue - the value that can be transfer along with the code execution * @param inDataOffs - start of memory to be input data to the call * @param inDataSize - size of memory to be input data to the call * @param outDataOffs - start of memory to be output of the call * @param outDataSize - size of memory to be output data to the call */ - public void callToAddress(DataWord gas, DataWord toAddressDW, DataWord endowmentValue, + public void callToAddress(DataWord gas, DataWord toAddressDW, DataWord storageAddressDW, DataWord endowmentValue, DataWord inDataOffs, DataWord inDataSize,DataWord outDataOffs, DataWord outDataSize) { + // TODO: update code for CALLSTATELESS + ByteBuffer data = memoryChunk(inDataOffs, inDataSize); // FETCH THE SAVED STORAGE 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 6b3ae6f9..aecdd852 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -144,7 +144,7 @@ public class VM { program.stackRequire(3); newMemSize = stack.peek().value().add(stack.get(stack.size()-3).value()); break; - case CALL: + case CALL: case CALLSTATELESS: program.stackRequire(7); gasCost = GasCost.CALL; BigInteger callGasWord = stack.get(stack.size()-1).value(); @@ -156,12 +156,6 @@ public class VM { BigInteger y = stack.get(stack.size()-6).value().add(stack.get(stack.size()-7).value()); // out offset+size newMemSize = x.max(y); break; - case CALLSTATELESS: - program.stackRequire(7); -// TODO -// runGas = c_callGas + m_stack[m_stack.size() - 1]; -// newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); - break; case POST: program.stackRequire(5); // TODO @@ -478,12 +472,18 @@ public class VM { program.step(); } break; case ADDMOD:{ - program.stackRequire(3); - // TODO: Implement new opcodes + program.stackRequire(3); + DataWord word1 = program.stackPop(); + word1.add(program.stackPop()); + word1.mod(program.stackPop()); + program.stackPush(word1); } break; case MULMOD:{ - program.stackRequire(3); - // TODO: Implement new opcodes + program.stackRequire(3); + DataWord word1 = program.stackPop(); + word1.mul(program.stackPop()); + word1.mod(program.stackPop()); + program.stackPush(word1); } break; /** @@ -862,7 +862,7 @@ public class VM { program.step(); } break; - case CALL: { + case CALL: case CALLSTATELESS: { program.stackRequire(7); DataWord gas = program.stackPop(); DataWord toAddress = program.stackPop(); @@ -885,16 +885,35 @@ public class VM { program.invokeData.getCallDeep(), hint); } - program.callToAddress(gas, toAddress, value, inDataOffs, inDataSize, outDataOffs, outDataSize); + program.callToAddress(gas, toAddress, op.equals(CALL) ? toAddress + : program.getOwnerAddress(), value, inDataOffs, + inDataSize, outDataOffs, outDataSize); program.step(); } break; case POST:{ program.stackRequire(5); - // TODO: Implement POST execution - } break; - case CALLSTATELESS:{ - // TODO: Implement CALLSTATELESS (almost same as CALL) + DataWord gas = program.stackPop(); + DataWord toAddress = program.stackPop(); + DataWord value = program.stackPop(); + + DataWord inDataOffs = program.stackPop(); + DataWord inDataSize = program.stackPop(); + + if (logger.isInfoEnabled()) { + hint = "addr: " + Hex.toHexString(toAddress.getLast20Bytes()) + + " gas: " + gas.shortHex() + + " inOff: " + inDataOffs.shortHex() + + " inSize: " + inDataSize.shortHex(); + logger.info(logString, program.getPC(), + String.format("%-12s", op.name()), + program.getGas().value(), + program.invokeData.getCallDeep(), hint); + } + + program.postToAddress(gas, toAddress, toAddress, value, inDataOffs, inDataSize); + + program.step(); } break; case RETURN:{ program.stackRequire(2); 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 3a4d6ac7..0653a119 100644 --- a/ethereumj-core/src/test/java/org/ethereum/vm/VMTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/vm/VMTest.java @@ -1177,7 +1177,7 @@ public class VMTest { * * @param n in DUPn */ - public void testDUPN_1(int n) { + private void testDUPN_1(int n) { VM vm = new VM(); byte operation = (byte) (OpCode.DUP1.val() + n - 1); @@ -1218,7 +1218,7 @@ public class VMTest { @Test // SWAP1...SWAP16 OP public void testSWAPS() { for (int i = 1; i < 17; i++) { - testSWAPN(i); + testSWAPN_1(i); } } @@ -1227,7 +1227,7 @@ public class VMTest { * * @param n in SWAPn */ - public void testSWAPN(int n) { + private void testSWAPN_1(int n) { VM vm = new VM(); byte operation = (byte) (OpCode.SWAP1.val() + n - 1); @@ -1253,7 +1253,7 @@ public class VMTest { } @Test(expected=StackTooSmallException.class) // SWAPN OP mal data - public void testSWAPN_3() { + public void testSWAPN_2() { VM vm = new VM(); Program program = new Program(Hex.decode("90"), new ProgramInvokeMockImpl()); @@ -1845,9 +1845,70 @@ public class VMTest { assertTrue(program.isStopped()); } } + + @Test // ADDMOD OP mal + public void testADDMOD_1() { + VM vm = new VM(); + Program program = new Program(Hex.decode("60026002600314"), new ProgramInvokeMockImpl()); + String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000001"; - @Test // MULL OP - public void testMULL_1() { + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + DataWord item1 = program.stack.pop(); + program.getResult().getRepository().close(); + assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); + } + + @Test // ADDMOD OP + public void testADDMOD_2() { + VM vm = new VM(); + Program program = new Program(Hex.decode("611000600261100214"), new ProgramInvokeMockImpl()); + String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000004"; + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + DataWord item1 = program.stack.pop(); + program.getResult().getRepository().close(); + assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); + } + + @Test // ADDMOD OP + public void testADDMOD_3() { + VM vm = new VM(); + Program program = new Program(Hex.decode("61100265123456789009600214"), new ProgramInvokeMockImpl()); + String s_expected_1 = "000000000000000000000000000000000000000000000000000000000000093B"; + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + DataWord item1 = program.stack.pop(); + program.getResult().getRepository().close(); + assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); + } + + @Test(expected=StackTooSmallException.class) // ADDMOD OP mal + public void testADDMOD_4() { + VM vm = new VM(); + Program program = new Program(Hex.decode("61123414"), new ProgramInvokeMockImpl()); + try { + vm.step(program); + vm.step(program); + } finally { + program.getResult().getRepository().close(); + assertTrue(program.isStopped()); + } + } + + @Test // MUL OP + public void testMUL_1() { VM vm = new VM(); Program program = new Program(Hex.decode("6003600202"), new ProgramInvokeMockImpl()); @@ -1862,8 +1923,8 @@ public class VMTest { assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); } - @Test // MULL OP - public void testMULL_2() { + @Test // MUL OP + public void testMUL_2() { VM vm = new VM(); Program program = new Program(Hex.decode("62222222600302"), new ProgramInvokeMockImpl()); @@ -1878,8 +1939,8 @@ public class VMTest { assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); } - @Test // MULL OP - public void testMULL_3() { + @Test // MUL OP + public void testMUL_3() { VM vm = new VM(); Program program = new Program(Hex.decode("622222226233333302"), new ProgramInvokeMockImpl()); @@ -1894,8 +1955,8 @@ public class VMTest { assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); } - @Test(expected=StackTooSmallException.class) // MULL OP mal - public void testMULL_4() { + @Test(expected=StackTooSmallException.class) // MUL OP mal + public void testMUL_4() { VM vm = new VM(); Program program = new Program(Hex.decode("600102"), new ProgramInvokeMockImpl()); @@ -1907,6 +1968,67 @@ public class VMTest { assertTrue(program.isStopped()); } } + + @Test // MULMOD OP + public void testMULMOD_1() { + VM vm = new VM(); + Program program = new Program(Hex.decode("60036002600415"), new ProgramInvokeMockImpl()); + String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000002"; + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + DataWord item1 = program.stack.pop(); + program.getResult().getRepository().close(); + assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); + } + + @Test // MULMOD OP + public void testMULMOD_2() { + VM vm = new VM(); + Program program = new Program(Hex.decode("622222226003600415"), new ProgramInvokeMockImpl()); + String s_expected_1 = "000000000000000000000000000000000000000000000000000000000000000C"; + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + DataWord item1 = program.stack.pop(); + program.getResult().getRepository().close(); + assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); + } + + @Test // MULMOD OP + public void testMULMOD_3() { + VM vm = new VM(); + Program program = new Program(Hex.decode("62222222623333336244444415"), new ProgramInvokeMockImpl()); + String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000000"; + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + DataWord item1 = program.stack.pop(); + program.getResult().getRepository().close(); + assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); + } + + @Test(expected=StackTooSmallException.class) // MULMOD OP mal + public void testMULMOD_4() { + VM vm = new VM(); + Program program = new Program(Hex.decode("600115"), new ProgramInvokeMockImpl()); + try { + vm.step(program); + vm.step(program); + } finally { + program.getResult().getRepository().close(); + assertTrue(program.isStopped()); + } + } @Test // DIV OP public void testDIV_1() { @@ -2834,14 +2956,14 @@ public class VMTest { program.getResult().getRepository().close(); assertTrue(program.isStopped()); } - } - + } + @Test // MOD OP public void testMOD_1() { VM vm = new VM(); Program program = new Program(Hex.decode("6003600406"), new ProgramInvokeMockImpl()); String s_expected_1 = "0000000000000000000000000000000000000000000000000000000000000001"; - + vm.step(program); vm.step(program); vm.step(program);