diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/GasCost.java b/ethereumj-core/src/main/java/org/ethereum/vm/GasCost.java index 594b83a9..f14495c4 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/GasCost.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/GasCost.java @@ -10,18 +10,18 @@ public class GasCost { /** Cost 1 gas */ public static int STEP = 1; + /** Cost 20 gas */ + public static int BALANCE = 20; + /** Cost 20 gas */ + public static int SHA3 = 20; + /** Cost 20 gas */ + public static int SLOAD = 20; /** Cost 0 gas */ public static int STOP = 0; /** Cost 0 gas */ public static int SUICIDE = 0; - /** Cost 20 gas */ - public static int SLOAD = 20; - /** Cost 20 gas */ - public static int SHA3 = 20; /** Cost 100 gas */ public static int SSTORE = 100; - /** Cost 20 gas */ - public static int BALANCE = 20; /** Cost 100 gas */ public static int CREATE = 100; /** Cost 20 gas */ @@ -32,4 +32,10 @@ public class GasCost { public static int TXDATA = 5; /** Cost 500 gas */ public static int TRANSACTION = 500; + /** Cost 32 gas */ + public static int LOG_GAS = 32; + /** Cost 1 gas */ + public static int LOG_DATA_GAS = 1; + /** Cost 32 gas */ + public static int LOG_TOPIC_GAS = 32; } \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/LogInfo.java b/ethereumj-core/src/main/java/org/ethereum/vm/LogInfo.java new file mode 100644 index 00000000..b3eded70 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/vm/LogInfo.java @@ -0,0 +1,60 @@ +package org.ethereum.vm; + +import org.spongycastle.util.encoders.Hex; + +import java.util.Arrays; +import java.util.List; + +/** + * www.etherj.com + * + * @author: Roman Mandeleil + * Created on: 19/11/2014 22:03 + */ + +public class LogInfo { + + byte[] address; + List topics; + byte[] data; + + public LogInfo(byte[] address, List topics, byte[] data) { + this.address = address; + this.topics = topics; + this.data = data; + } + + public byte[] getAddress() { + return address; + } + + public List getTopics() { + return topics; + } + + public byte[] getData() { + return data; + } + + @Override + public String toString() { + + StringBuffer topicsStr = new StringBuffer(); + topicsStr.append("["); + + for (byte[] topic: topics){ + String topicStr = Hex.toHexString(topic); + topicsStr.append(topicStr).append(" "); + } + topicsStr.append("]"); + + + return "LogInfo{" + + "address=" + Hex.toHexString(address) + + ", topics=" + topicsStr + + ", data=" + Hex.toHexString(data) + + '}'; + } + + +} diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java b/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java index 121f33bf..cb35a2a4 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java @@ -292,6 +292,13 @@ public enum OpCode { /** (0x9f) Exchange 17th item from stack with the top */ SWAP16(0x9f, 17), + /** (0xa[n]) log some data for some addres with 0..n tags [addr [tag0..tagn] data] */ + LOG0(0xa0, 2), + LOG1(0xa1, 3), + LOG2(0xa2, 4), + LOG3(0xa3, 5), + LOG4(0xa4, 6), + /* System operations */ /** (0xf0) Create a new account with associated code */ 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 f3498206..8b5f2173 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -316,6 +316,7 @@ public class Program { vm.play(program); result = program.getResult(); this.result.addDeleteAccounts(result.getDeleteAccounts()); + this.result.addLogInfos(result.getLogInfoList()); } if (result != null && @@ -415,6 +416,7 @@ public class Program { result = program.getResult(); this.getProgramTrace().merge(program.getProgramTrace()); this.result.addDeleteAccounts(result.getDeleteAccounts()); + this.result.addLogInfos(result.getLogInfoList()); } if (result != null && 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 a74068e1..66c1bf97 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramResult.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramResult.java @@ -17,6 +17,7 @@ public class ProgramResult { private ByteBuffer hReturn = null; private RuntimeException exception; private List deleteAccounts; + private List logInfoList; private Repository repository = null; @@ -69,6 +70,20 @@ public class ProgramResult { deleteAccounts.add(address); } + public void addLogInfo(LogInfo logInfo){ + if (this.logInfoList == null) logInfoList = new ArrayList<>(); + this.logInfoList.add(logInfo); + } + + public void addLogInfos(List logInfos){ + if (this.logInfoList == null) logInfoList = new ArrayList<>(); + this.logInfoList.addAll(logInfos); + } + + public List getLogInfoList() { + return logInfoList; + } + public void addDeleteAccounts(List accounts) { if (accounts == null) return; if (deleteAccounts == null) 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 6389dc2b..490f222b 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -73,8 +73,6 @@ public class VM { public void step(Program program) { - program.fullTrace(); - if (CONFIG.vmTrace()) program.saveOpTrace(); @@ -160,6 +158,16 @@ public class VM { gasCost = GasCost.CREATE; newMemSize = memNeeded(stack.get(stack.size()-2), stack.get(stack.size()-3)); break; + case LOG0: case LOG1: case LOG2: case LOG3: case LOG4: + + int nTopics = op.val() - OpCode.LOG0.val(); + newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-2)); + gasCost = GasCost.LOG_GAS + + GasCost.LOG_TOPIC_GAS * nTopics + + GasCost.LOG_DATA_GAS * stack.get(stack.size()-2).longValue(); + + break; + default: break; } @@ -717,6 +725,32 @@ public class VM { stack.set(stack.size() - n, word_1); program.step(); + } break; + case LOG0: case LOG1: case LOG2: case LOG3: case LOG4:{ + + DataWord address = program.programAddress; + + DataWord memStart = stack.pop(); + DataWord memOffset = stack.pop(); + + int nTopics = op.val() - OpCode.LOG0.val(); + + List topics = new ArrayList<>(); + for (int i = 0; i < nTopics; ++i){ + DataWord topic = stack.pop(); + topics.add(topic.getData()); + } + + ByteBuffer data = program.memoryChunk(memStart, memOffset); + + LogInfo logInfo = + new LogInfo(address.getLast20Bytes(), topics, data.array()); + + if (logger.isInfoEnabled()) + hint = logInfo.toString(); + + program.getResult().addLogInfo(logInfo); + } break; case MLOAD:{ DataWord addr = program.stackPop(); diff --git a/ethereumj-core/src/test/java/test/ethereum/vm/ProgramMemoryTest.java b/ethereumj-core/src/test/java/test/ethereum/vm/ProgramMemoryTest.java index ee2554f6..f01d0845 100644 --- a/ethereumj-core/src/test/java/test/ethereum/vm/ProgramMemoryTest.java +++ b/ethereumj-core/src/test/java/test/ethereum/vm/ProgramMemoryTest.java @@ -321,5 +321,16 @@ public class ProgramMemoryTest { int size = 16; program.allocateMemory(offset, size); assertEquals(32,program.getMemSize()); - } + } + + @Test + public void testInitialInsert() { + + + // todo: fix the array out of bound here + int offset = 32; + int size = 00; + program.memorySave(32, 0, new byte[0]); + assertEquals(32,program.getMemSize()); + } } \ No newline at end of file diff --git a/ethereumj-core/src/test/java/test/ethereum/vm/VMTest.java b/ethereumj-core/src/test/java/test/ethereum/vm/VMTest.java index 82c63181..f089a682 100644 --- a/ethereumj-core/src/test/java/test/ethereum/vm/VMTest.java +++ b/ethereumj-core/src/test/java/test/ethereum/vm/VMTest.java @@ -12,6 +12,8 @@ import org.junit.Test; import org.junit.runners.MethodSorters; import org.spongycastle.util.encoders.Hex; +import java.util.List; + import static org.junit.Assert.*; /** @@ -1211,6 +1213,125 @@ public class VMTest { assertEquals(expected, Hex.toHexString(program.getMemory().array())); } + + @Test // LOG0 OP + public void tesLog0(){ + + VM vm = new VM(); + program = new Program(Hex.decode("61123460005260206000A0"), invoke); + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + List logInfoList = program.getResult().getLogInfoList(); + LogInfo logInfo = logInfoList.get(0); + + assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", Hex.toHexString(logInfo.getAddress())); + assertEquals(0, logInfo.getTopics().size()); + assertEquals("0000000000000000000000000000000000000000000000000000000000001234", Hex.toHexString(logInfo.getData())); + } + + @Test // LOG1 OP + public void tesLog1(){ + + VM vm = new VM(); + program = new Program(Hex.decode("61123460005261999960206000A1"), invoke); + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + List logInfoList = program.getResult().getLogInfoList(); + LogInfo logInfo = logInfoList.get(0); + + assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", Hex.toHexString(logInfo.getAddress())); + assertEquals(1, logInfo.getTopics().size()); + assertEquals("0000000000000000000000000000000000000000000000000000000000001234", Hex.toHexString(logInfo.getData())); + } + + @Test // LOG2 OP + public void tesLog2(){ + + VM vm = new VM(); + program = new Program(Hex.decode("61123460005261999961666660206000A2"), invoke); + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + List logInfoList = program.getResult().getLogInfoList(); + LogInfo logInfo = logInfoList.get(0); + + assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", Hex.toHexString(logInfo.getAddress())); + assertEquals(2, logInfo.getTopics().size()); + assertEquals("0000000000000000000000000000000000000000000000000000000000001234", Hex.toHexString(logInfo.getData())); + } + + @Test // LOG3 OP + public void tesLog3(){ + + VM vm = new VM(); + program = new Program(Hex.decode("61123460005261999961666661333360206000A3"), invoke); + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + List logInfoList = program.getResult().getLogInfoList(); + LogInfo logInfo = logInfoList.get(0); + + assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", Hex.toHexString(logInfo.getAddress())); + assertEquals(3, logInfo.getTopics().size()); + assertEquals("0000000000000000000000000000000000000000000000000000000000001234", Hex.toHexString(logInfo.getData())); + } + + + @Test // LOG4 OP + public void tesLog4(){ + + VM vm = new VM(); + program = new Program(Hex.decode("61123460005261999961666661333361555560206000A4"), invoke); + + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + vm.step(program); + + List logInfoList = program.getResult().getLogInfoList(); + LogInfo logInfo = logInfoList.get(0); + + assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", Hex.toHexString(logInfo.getAddress())); + assertEquals(4, logInfo.getTopics().size()); + assertEquals("0000000000000000000000000000000000000000000000000000000000001234", Hex.toHexString(logInfo.getData())); + } + + + @Test // MSTORE OP public void testMSTORE_2() {