From 0b02376c175d23fa2746528160402a6fd76ad285 Mon Sep 17 00:00:00 2001 From: romanman Date: Sun, 22 Jun 2014 20:20:03 +0100 Subject: [PATCH] CALL op with in/out data + support for return + VMComplexText test3 demonstrates the return applied --- ethereumj-core/TODO.md | 11 +- .../org/ethereum/gui/ProgramPlayDialog.java | 2 +- .../org/ethereum/manager/WorldManager.java | 4 +- .../main/java/org/ethereum/vm/Program.java | 20 ++- .../java/org/ethereum/vm/ProgramResult.java | 2 +- .../java/org/ethereum/vm/VMComplexTest.java | 118 +++++++++++++++++- .../src/test/java/org/ethereum/vm/VMTest.java | 8 +- 7 files changed, 148 insertions(+), 17 deletions(-) diff --git a/ethereumj-core/TODO.md b/ethereumj-core/TODO.md index b78197fa..7b95417c 100644 --- a/ethereumj-core/TODO.md +++ b/ethereumj-core/TODO.md @@ -4,15 +4,16 @@ ---------------- - [x] **VM execution:** support CALL op -- [ ] **VM execution:** support CALL op with in/out data +- [x] **VM execution:** support CALL op with in/out data - [ ] **VM execution:** support CREATE op +- [ ] **VM execution:** SUICIDE op adjust - [ ] **ProgramPlayDialog** support internal calls - [ ] **Build:** extract core module and studio application - [ ] **Performance:** BigInteger math change for constant arrays implementation economy for memory allocation - [ ] **Command Line:** add the headless run option - [ ] **Testing by JSON files:** follow cpp client performs test case by getting json file contains the test describe -- [ ] Serpent new syntax: +- [ ] **SerpentCompiler** Serpent new syntax: (@> @< @/ @%) - unsigned operations > < / % - default are all signed operations += -= *= /= %= @/= @%= - short form operations @@ -23,7 +24,9 @@ share - code section ##### UnitTest: ---------------- -- [ ] **VM complex: CALL testing for in/out arrays -- [ ] **VM complex: CREATE testing +- [x] **VM complex:** CALL testing for in arrays +- [x] **VM complex:** CALL testing for out result +- [ ] **VM complex:** CREATE testing +- [ ] **VM complex:** SUICIDE testing - [ ] **WorldManager** apply transactions diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ProgramPlayDialog.java b/ethereumj-core/src/main/java/org/ethereum/gui/ProgramPlayDialog.java index ccab288a..c31c770a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ProgramPlayDialog.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ProgramPlayDialog.java @@ -198,7 +198,7 @@ public class ProgramPlayDialog extends JPanel implements ActionListener, //Schedule a job for the event-dispatching thread: //creating and showing this application's GUI. - String asmCode ="0 31 MSTORE8 224 MSIZE 224 MSIZE MSTORE 0 192 MSIZE ADD MSTORE8 96 MSIZE 32 ADD MSIZE DUP 32 ADD 11 SWAP MSTORE DUP 64 ADD 22 SWAP MSTORE DUP 96 ADD 33 SWAP MSTORE 128 SWAP MSTORE 0 752278364205682983151548199104072833112320979438 1000 CALL 32 0 MUL 160 ADD 32 ADD MLOAD 0 MSTORE"; + String asmCode ="11 0 MSTORE 22 32 MSTORE 33 64 MSTORE 44 96 MSTORE 55 128 MSTORE 66 160 MSTORE 192 0 RETURN"; final byte[] code = SerpentCompiler.compileAssemblyToMachine(asmCode); diff --git a/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java b/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java index d0382e25..db428340 100644 --- a/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java +++ b/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java @@ -286,9 +286,9 @@ public class WorldManager { // Save the code created by init byte[] bodyCode = null; - if (result.gethReturn() != null){ + if (result.getHReturn() != null){ - bodyCode = result.gethReturn().array(); + bodyCode = result.getHReturn().array(); } BigInteger gasPrice = 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 a5327d8c..1f9e60c7 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -323,9 +323,20 @@ public class Program { return; } - // todo: apply results: result.gethReturn() - // todo: if there is out specified place hReturn on the out + // 3. APPLY RESULTS: result.getHReturn() into out_memory allocated + ByteBuffer buffer = result.getHReturn(); + if (buffer != null){ + int retSize = buffer.array().length; + int allocSize = outDataSize.intValue(); + if (retSize > allocSize){ + + byte[] outArray = Arrays.copyOf(buffer.array(), allocSize ); + this.memorySave(outArray, buffer.array()); + } else{ + this.memorySave(outDataOffs.getData(), buffer.array()); + } + } detailDB.commitTrack(); chainDB.commitTrack(); @@ -333,6 +344,7 @@ public class Program { stackPush(new DataWord(1)); // the gas spent in any internal outcome + // even if execution was halted by an exception spendGas(result.getGasUsed(), " 'Total for CALL run' "); logger.info("The usage of the gas in external call updated", result.getGasUsed()); @@ -536,8 +548,8 @@ public class Program { globalOutput.append(" -- MEMORY -- ").append(memoryData).append("\n"); globalOutput.append(" -- STORAGE -- ").append(storageData).append("\n"); - if (result.gethReturn() != null){ - globalOutput.append("\n HReturn: ").append(Hex.toHexString(result.gethReturn().array())); + if (result.getHReturn() != null){ + globalOutput.append("\n HReturn: ").append(Hex.toHexString(result.getHReturn().array())); } // soffisticated assumption that msg.data != codedata diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramResult.java b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramResult.java index 06ed6ac1..967599c4 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramResult.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramResult.java @@ -32,7 +32,7 @@ public class ProgramResult { this.hReturn.put(hReturn); } - public ByteBuffer gethReturn() { + public ByteBuffer getHReturn() { return hReturn; } diff --git a/ethereumj-core/src/test/java/org/ethereum/vm/VMComplexTest.java b/ethereumj-core/src/test/java/org/ethereum/vm/VMComplexTest.java index be37a1d2..3ee2f520 100644 --- a/ethereumj-core/src/test/java/org/ethereum/vm/VMComplexTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/vm/VMComplexTest.java @@ -114,7 +114,7 @@ public class VMComplexTest { - @Test // contract call recursive with data + @Test // contractB call contractA with data to storage public void test2(){ /** @@ -216,10 +216,126 @@ public class VMComplexTest { assertEquals(expectedVal_1, value_1.longValue()); assertEquals(expectedVal_2, value_2.longValue()); + } + @Test // contractB call contractA with return expectation + public void test3(){ + + /** + * #The code will run + * ------------------ + + contract A: 77045e71a7a2c50903d88e564cd72fab11e82051 + --------------- + + a = 11 + b = 22 + c = 33 + d = 44 + e = 55 + f = 66 + + [asm 192 0 RETURN asm] + + + + contract B: 83c5541a6c8d2dbad642f385d8d06ca9b6c731ee + ----------- + a = msg((tx.gas / 10 * 8), 0x77045e71a7a2c50903d88e564cd72fab11e82051, 0, [11, 22, 33], 3, 6) + + */ + + + long expectedVal_1 = 11; + long expectedVal_2 = 22; + long expectedVal_3 = 33; + long expectedVal_4 = 44; + long expectedVal_5 = 55; + long expectedVal_6 = 66; + + // Set contract into Database + String callerAddr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826"; + + String contractA_addr = "77045e71a7a2c50903d88e564cd72fab11e82051"; + String contractB_addr = "83c5541a6c8d2dbad642f385d8d06ca9b6c731ee"; + + String code_a = "600b60005460166020546021604054602c6060546037608054604260a05460c06000f2"; + String code_b = "6000601f5560e05b60e05b54600060c05b015560605b6020015b51602001600b5254516040016016525451606001602152546080525460007377045e71a7a2c50903d88e564cd72fab11e820516103e8f1602060000260a00160200153600054"; + + byte[] caller_addr_bytes = Hex.decode(callerAddr); + + byte[] contractA_addr_bytes = Hex.decode(contractA_addr); + byte[] codeA = Hex.decode(code_a); + byte[] codeA_Key = HashUtil.sha3(codeA); + AccountState accountState_a = new AccountState(); + accountState_a.setCodeHash(codeA_Key); + WorldManager.instance.worldState.update(contractA_addr_bytes, accountState_a.getEncoded()); + + byte[] contractB_addr_bytes = Hex.decode(contractB_addr); + byte[] codeB = Hex.decode(code_b); + byte[] codeB_Key = HashUtil.sha3(codeB); + AccountState accountState_b = new AccountState(); + accountState_b.setCodeHash(codeB_Key); + WorldManager.instance.worldState.update(contractB_addr_bytes, accountState_a.getEncoded()); + + AccountState callerAcountState = new AccountState(); + callerAcountState.addToBalance(new BigInteger("100000000000000000000")); + WorldManager.instance.worldState.update(caller_addr_bytes, callerAcountState.getEncoded()); + + WorldManager.instance.chainDB.put(codeA_Key, codeA); + + TrackTrie stateDB = new TrackTrie(WorldManager.instance.worldState); + TrackDatabase chainDb = new TrackDatabase(WorldManager.instance.chainDB); + TrackDatabase detaildDB = new TrackDatabase(WorldManager.instance.detaildDB); + + ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl(); + pi.setDetaildDB(detaildDB); + pi.setChainDb(chainDb); + pi.setStateDB(stateDB); + pi.setDetails(null); + pi.setOwnerAddress(contractB_addr); + + // ****************** // + // Play the program // + // ****************** // + VM vm = new VM(); + Program program = new Program(codeB, pi); + + try { + while(!program.isStopped()) + vm.step(program); + } catch (RuntimeException e) { + program.setRuntimeFailure(e); + } + + + System.out.println(); + System.out.println("============ Results ============"); + AccountState as = + new AccountState(WorldManager.instance.worldState.get( + Hex.decode( contractA_addr) )); + + + System.out.println("*** Used gas: " + program.result.getGasUsed()); + + DataWord value1 = program.memoryLoad(new DataWord(32)); + DataWord value2 = program.memoryLoad(new DataWord(64)); + DataWord value3 = program.memoryLoad(new DataWord(96)); + DataWord value4 = program.memoryLoad(new DataWord(128)); + DataWord value5 = program.memoryLoad(new DataWord(160)); + DataWord value6 = program.memoryLoad(new DataWord(192)); + + assertEquals(expectedVal_1, value1.longValue()); + assertEquals(expectedVal_2, value2.longValue()); + assertEquals(expectedVal_3, value3.longValue()); + assertEquals(expectedVal_4, value4.longValue()); + assertEquals(expectedVal_5, value5.longValue()); + assertEquals(expectedVal_6, value6.longValue()); + } + } 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 75c92edf..535d71a9 100644 --- a/ethereumj-core/src/test/java/org/ethereum/vm/VMTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/vm/VMTest.java @@ -2141,7 +2141,7 @@ public class VMTest { vm.step(program); vm.step(program); - assertEquals(s_expected_1, Hex.toHexString(program.getResult().gethReturn().array()).toUpperCase()); + assertEquals(s_expected_1, Hex.toHexString(program.getResult().getHReturn().array()).toUpperCase()); assertTrue(program.stopped); } @@ -2160,7 +2160,7 @@ public class VMTest { vm.step(program); vm.step(program); - assertEquals(s_expected_1, Hex.toHexString(program.getResult().gethReturn().array()).toUpperCase()); + assertEquals(s_expected_1, Hex.toHexString(program.getResult().getHReturn().array()).toUpperCase()); assertTrue(program.stopped); } @@ -2180,7 +2180,7 @@ public class VMTest { vm.step(program); vm.step(program); - assertEquals(s_expected_1, Hex.toHexString(program.getResult().gethReturn().array()).toUpperCase()); + assertEquals(s_expected_1, Hex.toHexString(program.getResult().getHReturn().array()).toUpperCase()); assertTrue(program.stopped); } @@ -2201,7 +2201,7 @@ public class VMTest { vm.step(program); vm.step(program); - assertEquals(s_expected_1, Hex.toHexString(program.getResult().gethReturn().array()).toUpperCase()); + assertEquals(s_expected_1, Hex.toHexString(program.getResult().getHReturn().array()).toUpperCase()); assertTrue(program.stopped); }