From 48b27d38ece72a5b409009055b34dabceb4ec24a Mon Sep 17 00:00:00 2001 From: Roman Mandeleil Date: Thu, 8 Jan 2015 19:48:37 +0200 Subject: [PATCH] Validate JUMPDEST by pre-compile + tweak injection of blockStore by the new interface --- .../org/ethereum/core/BlockchainImpl.java | 9 +-- .../main/java/org/ethereum/db/BlockStore.java | 39 ++++++++++++ .../java/org/ethereum/db/BlockStoreDummy.java | 60 +++++++++++++++++++ .../java/org/ethereum/db/BlockStoreImpl.java | 18 +++++- .../org/ethereum/manager/WorldManager.java | 3 +- .../main/java/org/ethereum/vm/Program.java | 28 +++++++-- .../ethereum/vm/ProgramInvokeFactoryImpl.java | 3 - .../src/main/java/org/ethereum/vm/VM.java | 8 +-- .../java/test/ethereum/vm/VMCustomTest.java | 9 +-- .../test/java/test/ethereum/vm/VMTest.java | 2 + 10 files changed, 152 insertions(+), 27 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/core/BlockchainImpl.java b/ethereumj-core/src/main/java/org/ethereum/core/BlockchainImpl.java index 5de85deb..57559494 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/BlockchainImpl.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/BlockchainImpl.java @@ -1,6 +1,6 @@ package org.ethereum.core; -import org.ethereum.db.BlockStoreImpl; +import org.ethereum.db.BlockStore; import org.ethereum.facade.Blockchain; import org.ethereum.facade.Repository; import org.ethereum.listener.EthereumListener; @@ -9,12 +9,9 @@ import org.ethereum.net.BlockQueue; import org.ethereum.net.server.ChannelManager; import org.ethereum.util.AdvancedDeviceUtils; import org.ethereum.vm.ProgramInvokeFactory; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.spongycastle.util.encoders.Hex; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.FileSystemUtils; @@ -23,9 +20,7 @@ import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; - import java.math.BigInteger; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -79,7 +74,7 @@ public class BlockchainImpl implements Blockchain { private Repository track; @Autowired - private BlockStoreImpl blockStore; + private BlockStore blockStore; private Block bestBlock; private BigInteger totalDifficulty = BigInteger.ZERO; diff --git a/ethereumj-core/src/main/java/org/ethereum/db/BlockStore.java b/ethereumj-core/src/main/java/org/ethereum/db/BlockStore.java index 9365d79f..51839a9a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/db/BlockStore.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/BlockStore.java @@ -1,6 +1,11 @@ package org.ethereum.db; import org.ethereum.core.Block; +import org.ethereum.core.TransactionReceipt; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigInteger; +import java.util.List; /** * @author: Roman Mandeleil @@ -10,4 +15,38 @@ import org.ethereum.core.Block; public interface BlockStore { public byte[] getBlockHashByNumber(long blockNumber); + + @Transactional(readOnly = true) + Block getBlockByNumber(long blockNumber); + + @Transactional(readOnly = true) + Block getBlockByHash(byte[] hash); + + @Transactional(readOnly = true) + @SuppressWarnings("unchecked") + List getListOfHashesStartFrom(byte[] hash, int qty); + + @Transactional + void deleteBlocksSince(long number); + + @Transactional + void saveBlock(Block block, List receipts); + + @Transactional(readOnly = true) + BigInteger getTotalDifficultySince(long number); + + @Transactional(readOnly = true) + BigInteger getTotalDifficulty(); + + @Transactional(readOnly = true) + Block getBestBlock(); + + @Transactional(readOnly = true) + @SuppressWarnings("unchecked") + List getAllBlocks(); + + @Transactional + void reset(); + + TransactionReceipt getTransactionReceiptByHash(byte[] hash); } diff --git a/ethereumj-core/src/main/java/org/ethereum/db/BlockStoreDummy.java b/ethereumj-core/src/main/java/org/ethereum/db/BlockStoreDummy.java index 2e2f4b91..8bcf4373 100644 --- a/ethereumj-core/src/main/java/org/ethereum/db/BlockStoreDummy.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/BlockStoreDummy.java @@ -1,8 +1,13 @@ package org.ethereum.db; +import org.ethereum.core.Block; +import org.ethereum.core.TransactionReceipt; import org.ethereum.crypto.HashUtil; import org.ethereum.util.ByteUtil; +import java.math.BigInteger; +import java.util.List; + /** * @author: Roman Mandeleil * Created on: 08/01/2015 17:33 @@ -16,4 +21,59 @@ public class BlockStoreDummy implements BlockStore{ byte[] data = String.valueOf(blockNumber).getBytes(); return HashUtil.sha3(data); } + + @Override + public Block getBlockByNumber(long blockNumber) { + return null; + } + + @Override + public Block getBlockByHash(byte[] hash) { + return null; + } + + @Override + public List getListOfHashesStartFrom(byte[] hash, int qty) { + return null; + } + + @Override + public void deleteBlocksSince(long number) { + + } + + @Override + public void saveBlock(Block block, List receipts) { + + } + + @Override + public BigInteger getTotalDifficultySince(long number) { + return null; + } + + @Override + public BigInteger getTotalDifficulty() { + return null; + } + + @Override + public Block getBestBlock() { + return null; + } + + @Override + public List getAllBlocks() { + return null; + } + + @Override + public void reset() { + + } + + @Override + public TransactionReceipt getTransactionReceiptByHash(byte[] hash) { + return null; + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/db/BlockStoreImpl.java b/ethereumj-core/src/main/java/org/ethereum/db/BlockStoreImpl.java index 37a2f6db..0fe0b777 100644 --- a/ethereumj-core/src/main/java/org/ethereum/db/BlockStoreImpl.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/BlockStoreImpl.java @@ -21,7 +21,7 @@ import java.util.List; * @author Roman Mandeleil * @since 12.11.2014 */ -@Repository +@Repository("blockStore") @Transactional(propagation = Propagation.SUPPORTS) public class BlockStoreImpl implements BlockStore{ @@ -31,14 +31,18 @@ public class BlockStoreImpl implements BlockStore{ @Autowired ApplicationContext ctx; + public BlockStoreImpl() { + } + @Override public byte[] getBlockHashByNumber(long blockNumber) { - + Block block = getBlockByNumber(blockNumber); if (block != null) return block.getHash(); return ByteUtil.EMPTY_BYTE_ARRAY; } + @Override @Transactional(readOnly = true) public Block getBlockByNumber(long blockNumber) { @@ -52,6 +56,7 @@ public class BlockStoreImpl implements BlockStore{ return new Block(vo.rlp); } + @Override @Transactional(readOnly = true) public Block getBlockByHash(byte[] hash) { @@ -65,6 +70,7 @@ public class BlockStoreImpl implements BlockStore{ return new Block(vo.rlp); } + @Override @Transactional(readOnly = true) @SuppressWarnings("unchecked") public List getListOfHashesStartFrom(byte[] hash, int qty) { @@ -87,6 +93,7 @@ public class BlockStoreImpl implements BlockStore{ return hashes; } + @Override @Transactional public void deleteBlocksSince(long number) { @@ -97,6 +104,7 @@ public class BlockStoreImpl implements BlockStore{ } + @Override @Transactional public void saveBlock(Block block, List receipts) { @@ -115,6 +123,7 @@ public class BlockStoreImpl implements BlockStore{ sessionFactory.getCurrentSession().persist(blockVO); } + @Override @Transactional(readOnly = true) public BigInteger getTotalDifficultySince(long number) { @@ -127,6 +136,7 @@ public class BlockStoreImpl implements BlockStore{ } + @Override @Transactional(readOnly = true) public BigInteger getTotalDifficulty() { @@ -137,6 +147,7 @@ public class BlockStoreImpl implements BlockStore{ } + @Override @Transactional(readOnly = true) public Block getBestBlock() { @@ -151,6 +162,7 @@ public class BlockStoreImpl implements BlockStore{ return new Block(vo.rlp); } + @Override @Transactional(readOnly = true) @SuppressWarnings("unchecked") public List getAllBlocks() { @@ -166,12 +178,14 @@ public class BlockStoreImpl implements BlockStore{ return blocks; } + @Override @Transactional public void reset() { sessionFactory.getCurrentSession(). createQuery("delete from BlockVO").executeUpdate(); } + @Override public TransactionReceipt getTransactionReceiptByHash(byte[] hash) { List result = sessionFactory.getCurrentSession(). 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 5825ba87..3b3fb19a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java +++ b/ethereumj-core/src/main/java/org/ethereum/manager/WorldManager.java @@ -6,6 +6,7 @@ import org.ethereum.core.Transaction; import org.ethereum.core.TransactionReceipt; import org.ethereum.core.Wallet; import org.ethereum.crypto.HashUtil; +import org.ethereum.db.BlockStore; import org.ethereum.db.BlockStoreImpl; import org.ethereum.facade.Blockchain; import org.ethereum.facade.Repository; @@ -63,7 +64,7 @@ public class WorldManager { private PeerDiscovery peerDiscovery; @Autowired - private BlockStoreImpl blockStore; + private BlockStore blockStore; @Autowired private ChannelManager channelManager; 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 b81b7ed6..d4a34a7d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/Program.java @@ -27,11 +27,7 @@ import java.math.BigInteger; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Stack; +import java.util.*; import static org.ethereum.config.SystemProperties.CONFIG; import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; @@ -70,6 +66,8 @@ public class Program { byte lastOp = 0; byte previouslyExecutedOp = 0; boolean stopped = false; + + private Set jumpdest = new HashSet<>(); ProgramInvoke invokeData; @@ -83,6 +81,7 @@ public class Program { this.programAddress = invokeData.getOwnerAddress(); this.invokeHash = invokeData.hashCode(); this.result.setRepository(invokeData.getRepository()); + precompile(); } } @@ -856,6 +855,21 @@ public class Program { return programTrace; } + public void precompile(){ + for (int i = 0; i < ops.length; ++i){ + + OpCode op = OpCode.code(ops[i]); + if (op == null) continue; + + if (op.equals(OpCode.JUMPDEST)) jumpdest.add(i); + + if (op.asInt() >= OpCode.PUSH1.asInt() && op.asInt() <= OpCode.PUSH32.asInt()){ + i += op.asInt() - OpCode.PUSH1.asInt() + 1; + } + } + } + + public static String stringify(byte[] code, int index, String result) { if (code == null || code.length == 0) return result; @@ -889,6 +903,10 @@ public class Program { this.listener = listener; } + public void vallidateJumpDest(int nextPC) { + if (!jumpdest.contains(nextPC)) throw new BadJumpDestinationException(); + } + public interface ProgramListener { public void output(String out); } diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeFactoryImpl.java b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeFactoryImpl.java index f255bee2..5fa54ba5 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeFactoryImpl.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/ProgramInvokeFactoryImpl.java @@ -29,9 +29,6 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory { @Autowired private Blockchain blockchain; - - @Autowired - private BlockStoreImpl blockStore; // Invocation by the wire tx 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 a5e8b9e1..8ed4db8f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -896,9 +896,8 @@ public class VM { case JUMP: { DataWord pos = program.stackPop(); int nextPC = pos.intValue(); // possible overflow - if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val()) - throw program.new BadJumpDestinationException(); - + program.vallidateJumpDest(nextPC); + if (logger.isInfoEnabled()) hint = "~> " + nextPC; @@ -913,8 +912,7 @@ public class VM { if (!cond.isZero()) { int nextPC = pos.intValue(); // possible overflow - if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val()) - throw program.new BadJumpDestinationException(); + program.vallidateJumpDest(nextPC); if (logger.isInfoEnabled()) hint = "~> " + nextPC; diff --git a/ethereumj-core/src/test/java/test/ethereum/vm/VMCustomTest.java b/ethereumj-core/src/test/java/test/ethereum/vm/VMCustomTest.java index 8c43785a..c1b29c97 100644 --- a/ethereumj-core/src/test/java/test/ethereum/vm/VMCustomTest.java +++ b/ethereumj-core/src/test/java/test/ethereum/vm/VMCustomTest.java @@ -399,14 +399,15 @@ public class VMCustomTest { } } - @Test // PREVHASH OP - public void testPREVHASH_1() { + @Test // BLOCKHASH OP + public void testBLOCKHASH_1() { VM vm = new VM(); program = - new Program(Hex.decode("40"), invoke); - String s_expected_1 = "961CB117ABA86D1E596854015A1483323F18883C2D745B0BC03E87F146D2BB1C"; + new Program(Hex.decode("600140"), invoke); + String s_expected_1 = "C89EFDAA54C0F20C7ADF612882DF0950F5A951637E0307CDCB4C672F298B8BC6"; + vm.step(program); vm.step(program); DataWord item1 = program.stackPop(); 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 aa8d3e49..aa724763 100644 --- a/ethereumj-core/src/test/java/test/ethereum/vm/VMTest.java +++ b/ethereumj-core/src/test/java/test/ethereum/vm/VMTest.java @@ -1,5 +1,6 @@ package test.ethereum.vm; +import jdk.nashorn.internal.ir.annotations.Ignore; import org.ethereum.facade.Repository; import org.ethereum.util.ByteUtil; import org.ethereum.vm.DataWord; @@ -2732,6 +2733,7 @@ public class VMTest { assertEquals(s_expected_1, Hex.toHexString(item1.getData()).toUpperCase()); } + @Ignore // todo: test is not testing EXTCODESIZE @Test // EXTCODESIZE OP public void testEXTCODESIZE_1() { VM vm = new VM();