From 3ad490bf3b3690434f90bd42a9f5be6280b2f1fb Mon Sep 17 00:00:00 2001 From: Anton Nahsatyrev Date: Wed, 16 Aug 2017 15:44:22 +0300 Subject: [PATCH 1/6] EIP-140 implementation --- .../org/ethereum/config/BlockchainConfig.java | 2 + .../config/blockchain/AbstractConfig.java | 5 ++ .../config/blockchain/ByzantiumConfig.java | 18 +++++ .../config/blockchain/Eip150HFConfig.java | 5 ++ .../ethereum/core/TransactionExecutor.java | 17 ++-- .../src/main/java/org/ethereum/vm/OpCode.java | 6 ++ .../src/main/java/org/ethereum/vm/VM.java | 22 +++-- .../java/org/ethereum/vm/program/Program.java | 80 +++++++++++-------- .../ethereum/vm/program/ProgramResult.java | 11 ++- .../jsontestsuite/GitHubJSONTestSuite.java | 1 + .../jsontestsuite/GitHubStateTest.java | 6 +- 11 files changed, 122 insertions(+), 51 deletions(-) create mode 100644 ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java diff --git a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java index 431d56a379..84f1256961 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java @@ -122,4 +122,6 @@ String validateTransactionChanges(BlockStore blockStore, Block curBlock, Transac * EIP155: https://github.com/ethereum/EIPs/issues/155 */ Integer getChainId(); + + boolean eip140(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java index 0d5a562a7b..1d2399fbee 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java @@ -160,6 +160,11 @@ public Integer getChainId() { return null; } + @Override + public boolean eip140() { + return false; + } + @Override public String toString() { return getClass().getSimpleName(); diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java new file mode 100644 index 0000000000..22151e4fe4 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java @@ -0,0 +1,18 @@ +package org.ethereum.config.blockchain; + +import org.ethereum.config.BlockchainConfig; + +/** + * Created by Anton Nashatyrev on 15.08.2017. + */ +public class ByzantiumConfig extends Eip160HFConfig { + + public ByzantiumConfig(BlockchainConfig parent) { + super(parent); + } + + @Override + public boolean eip140() { + return true; + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java index 7e5d1354f2..64f7bc9874 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java @@ -141,4 +141,9 @@ public Constants getCommonConstants() { public Integer getChainId() { return null; } + + @Override + public boolean eip140() { + return false; + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/ethereumj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index deb0095caa..8df8a7d8d0 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -300,7 +300,7 @@ public void go() { result = program.getResult(); m_endGas = toBI(tx.getGasLimit()).subtract(toBI(program.getResult().getGasUsed())); - if (tx.isContractCreation()) { + if (tx.isContractCreation() && !result.isRevert()) { int returnDataGasValue = getLength(program.getResult().getHReturn()) * blockchainConfig.getGasCost().getCREATE_DATA(); if (m_endGas.compareTo(BigInteger.valueOf(returnDataGasValue)) < 0) { @@ -331,19 +331,24 @@ public void go() { } - if (result.getException() != null) { + if (result.getException() != null || result.isRevert()) { result.getDeleteAccounts().clear(); result.getLogInfoList().clear(); result.resetFutureRefund(); + cacheTrack.rollback(); - throw result.getException(); + if (result.getException() != null) { + throw result.getException(); + } + } else { + touchedAccounts.addAll(result.getTouchedAccounts()); + cacheTrack.commit(); } - touchedAccounts.addAll(result.getTouchedAccounts()); + } else { + cacheTrack.commit(); } - cacheTrack.commit(); - } catch (Throwable e) { // TODO: catch whatever they will throw on you !!! 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 d6f68fd861..1ff33bd266 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java @@ -587,6 +587,12 @@ public enum OpCode { * also the Value parameter is omitted for this opCode */ DELEGATECALL(0xf4, 6, 1, SpecialTier), + /** + * (0xfd) The `REVERT` instruction will stop execution, roll back all state changes done so far + * and provide a pointer to a memory section, which can be interpreted as an error code or message. + * While doing so, it will not consume all the remaining gas. + */ + REVERT(0xfd, 2, 0, ZeroTier), /** * (0xff) Halt execution and register account for * later deletion 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 b0ac939654..70a064250e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -35,11 +35,7 @@ import static org.ethereum.crypto.HashUtil.sha3; import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; -import static org.ethereum.vm.OpCode.CALL; -import static org.ethereum.vm.OpCode.CALLCODE; -import static org.ethereum.vm.OpCode.CREATE; -import static org.ethereum.vm.OpCode.DELEGATECALL; -import static org.ethereum.vm.OpCode.PUSH1; +import static org.ethereum.vm.OpCode.*; /** * The Ethereum Virtual Machine (EVM) is responsible for initialization @@ -150,12 +146,16 @@ public void step(Program program) { OpCode op = OpCode.code(program.getCurrentOp()); if (op == null) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); - } - if (op == DELEGATECALL) { + } else if (op == DELEGATECALL) { // opcode since Homestead release only if (!blockchainConfig.getConstants().hasDelegateCallOpcode()) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); } + } else if (op == REVERT) { + // opcode since Bizantium HF only + if (!blockchainConfig.eip140()) { + throw Program.Exception.invalidOpCode(program.getCurrentOp()); + } } program.setLastOp(op.val()); @@ -236,6 +236,7 @@ else if (oldValue != null && newValue.isZero()) { gasCost += calcMemGas(gasCosts, oldMemSize, memNeeded(stack.peek(), new DataWord(32)), 0); break; case RETURN: + case REVERT: gasCost = gasCosts.getSTOP() + calcMemGas(gasCosts, oldMemSize, memNeeded(stack.peek(), stack.get(stack.size() - 2)), 0); break; @@ -1190,7 +1191,8 @@ else if (oldValue != null && newValue.isZero()) { program.step(); } break; - case RETURN: { + case RETURN: + case REVERT: { DataWord offset = program.stackPop(); DataWord size = program.stackPop(); @@ -1204,6 +1206,10 @@ else if (oldValue != null && newValue.isZero()) { program.step(); program.stop(); + + if (op == REVERT) { + program.getResult().setRevert(); + } } break; case SUICIDE: { diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/program/Program.java b/ethereumj-core/src/main/java/org/ethereum/vm/program/Program.java index 3d0d3972cc..f8904becd9 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -470,27 +470,7 @@ this, new DataWord(newAddress), getOwnerAddress(), value, gasLimit, getResult().merge(result); } - // 4. CREATE THE CONTRACT OUT OF RETURN - byte[] code = result.getHReturn(); - - long storageCost = getLength(code) * getBlockchainConfig().getGasCost().getCREATE_DATA(); - long afterSpend = programInvoke.getGas().longValue() - storageCost - result.getGasUsed(); - if (afterSpend < 0) { - if (!config.getBlockchainConfig().getConfigForBlock(getNumber().longValue()).getConstants().createEmptyContractOnOOG()) { - result.setException(Program.Exception.notEnoughSpendingGas("No gas to return just created contract", - storageCost, this)); - } else { - track.saveCode(newAddress, EMPTY_BYTE_ARRAY); - } - } else if (getLength(code) > blockchainConfig.getConstants().getMAX_CONTRACT_SZIE()) { - result.setException(Program.Exception.notEnoughSpendingGas("Contract size too large: " + getLength(result.getHReturn()), - storageCost, this)); - } else { - result.spendGas(storageCost); - track.saveCode(newAddress, code); - } - - if (result.getException() != null) { + if (result.getException() != null || result.isRevert()) { logger.debug("contract run halted by Exception: contract: [{}], exception: [{}]", Hex.toHexString(newAddress), result.getException()); @@ -500,14 +480,37 @@ this, new DataWord(newAddress), getOwnerAddress(), value, gasLimit, track.rollback(); stackPushZero(); - return; - } - if (!byTestingSuite()) - track.commit(); + if (result.getException() != null) { + return; + } + } else { + // 4. CREATE THE CONTRACT OUT OF RETURN + byte[] code = result.getHReturn(); + + long storageCost = getLength(code) * getBlockchainConfig().getGasCost().getCREATE_DATA(); + long afterSpend = programInvoke.getGas().longValue() - storageCost - result.getGasUsed(); + if (afterSpend < 0) { + if (!config.getBlockchainConfig().getConfigForBlock(getNumber().longValue()).getConstants().createEmptyContractOnOOG()) { + result.setException(Program.Exception.notEnoughSpendingGas("No gas to return just created contract", + storageCost, this)); + } else { + track.saveCode(newAddress, EMPTY_BYTE_ARRAY); + } + } else if (getLength(code) > blockchainConfig.getConstants().getMAX_CONTRACT_SZIE()) { + result.setException(Program.Exception.notEnoughSpendingGas("Contract size too large: " + getLength(result.getHReturn()), + storageCost, this)); + } else { + result.spendGas(storageCost); + track.saveCode(newAddress, code); + } - // IN SUCCESS PUSH THE ADDRESS INTO THE STACK - stackPush(new DataWord(newAddress)); + if (!byTestingSuite()) + track.commit(); + + // IN SUCCESS PUSH THE ADDRESS INTO THE STACK + stackPush(new DataWord(newAddress)); + } // 5. REFUND THE REMAIN GAS long refundGas = gasLimit.longValue() - result.getGasUsed(); @@ -594,7 +597,7 @@ this, new DataWord(contextAddress), getTrace().merge(program.getTrace()); getResult().merge(result); - if (result.getException() != null) { + if (result.getException() != null || result.isRevert()) { logger.debug("contract run halted by Exception: contract: [{}], exception: [{}]", Hex.toHexString(contextAddress), result.getException()); @@ -604,12 +607,25 @@ this, new DataWord(contextAddress), track.rollback(); stackPushZero(); - return; - } else if (byTestingSuite()) { + + if (result.getException() != null) { + return; + } + } else { + // 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK + track.commit(); + stackPushOne(); + } + + if (byTestingSuite()) { logger.info("Testing run, skipping storage diff listener"); } else if (Arrays.equals(transaction.getReceiveAddress(), internalTx.getReceiveAddress())) { storageDiffListener.merge(program.getStorageDiff()); } + } else { + // 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK + track.commit(); + stackPushOne(); } // 3. APPLY RESULTS: result.getHReturn() into out_memory allocated @@ -621,10 +637,6 @@ this, new DataWord(contextAddress), memorySaveLimited(offset, buffer, size); } - // 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK - track.commit(); - stackPushOne(); - // 5. REFUND THE REMAIN GAS if (result != null) { BigInteger refundGas = msg.getGas().value().subtract(toBI(result.getGasUsed())); diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/program/ProgramResult.java b/ethereumj-core/src/main/java/org/ethereum/vm/program/ProgramResult.java index e7150630e5..a0e7ad1d75 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/program/ProgramResult.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/program/ProgramResult.java @@ -40,6 +40,7 @@ public class ProgramResult { private long gasUsed; private byte[] hReturn = EMPTY_BYTE_ARRAY; private RuntimeException exception; + private boolean revert; private Set deleteAccounts; private ByteArraySet touchedAccounts = new ByteArraySet(); @@ -58,6 +59,14 @@ public void spendGas(long gas) { gasUsed += gas; } + public void setRevert() { + this.revert = true; + } + + public boolean isRevert() { + return revert; + } + public void refundGas(long gas) { gasUsed -= gas; } @@ -180,7 +189,7 @@ public void resetFutureRefund() { public void merge(ProgramResult another) { addInternalTransactions(another.getInternalTransactions()); - if (another.getException() == null) { + if (another.getException() == null && !isRevert()) { addDeleteAccounts(another.getDeleteAccounts()); addLogInfos(another.getLogInfoList()); addFutureRefund(another.getFutureRefund()); diff --git a/ethereumj-core/src/test/java/org/ethereum/jsontestsuite/GitHubJSONTestSuite.java b/ethereumj-core/src/test/java/org/ethereum/jsontestsuite/GitHubJSONTestSuite.java index 7c6a551f33..402d9086b3 100644 --- a/ethereumj-core/src/test/java/org/ethereum/jsontestsuite/GitHubJSONTestSuite.java +++ b/ethereumj-core/src/test/java/org/ethereum/jsontestsuite/GitHubJSONTestSuite.java @@ -271,6 +271,7 @@ public BlockchainNetConfig getConfig() { case Homestead: return new HomesteadConfig(); case EIP150: return new Eip150HFConfig(new DaoHFConfig()); case EIP158: return new Eip160HFConfig(new DaoHFConfig()); + case Byzantium: return new ByzantiumConfig(new DaoHFConfig()); default: throw new IllegalArgumentException("Unknown network value: " + this.name()); } } diff --git a/ethereumj-core/src/test/java/org/ethereum/jsontestsuite/GitHubStateTest.java b/ethereumj-core/src/test/java/org/ethereum/jsontestsuite/GitHubStateTest.java index 0ca90eac00..95e370b183 100644 --- a/ethereumj-core/src/test/java/org/ethereum/jsontestsuite/GitHubStateTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/jsontestsuite/GitHubStateTest.java @@ -35,7 +35,9 @@ public class GitHubStateTest { GitHubJSONTestSuite.Network.Frontier, GitHubJSONTestSuite.Network.Homestead, GitHubJSONTestSuite.Network.EIP150, - GitHubJSONTestSuite.Network.EIP158 + GitHubJSONTestSuite.Network.EIP158, +// GitHubJSONTestSuite.Network.Byzantium + }; static GeneralStateTestSuite suite; @@ -56,7 +58,7 @@ public void clean() { // it reduces impact on GitHub API public void stSingleTest() throws IOException { GeneralStateTestSuite.runSingle( - "stTransactionTest/zeroSigTransacrionCreate.json", commitSHA, GitHubJSONTestSuite.Network.Frontier); + "stRevertTest/RevertOpcodeInCreateReturns.json", commitSHA, GitHubJSONTestSuite.Network.Byzantium); } @Test From 51806a23e540bcdb164b83754177e83ec73c167d Mon Sep 17 00:00:00 2001 From: Anton Nahsatyrev Date: Wed, 16 Aug 2017 16:30:40 +0300 Subject: [PATCH 2/6] Fix merge error --- .../java/org/ethereum/config/blockchain/ByzantiumConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java index dd4151f174..04ea8e6f3e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java @@ -61,6 +61,7 @@ protected int getExplosion(BlockHeader curBlock, BlockHeader parent) { public BigInteger getCalcDifficultyMultiplier(BlockHeader curBlock, BlockHeader parent) { long unclesAdj = parent.hasUncles() ? 2 : 1; return BigInteger.valueOf(Math.max(unclesAdj - (curBlock.getTimestamp() - parent.getTimestamp()) / 9, -99)); + } @Override public boolean eip140() { From 46364b9019c094f8f9044b04185cd0e47ae3b1ea Mon Sep 17 00:00:00 2001 From: Anton Nahsatyrev Date: Wed, 16 Aug 2017 18:22:09 +0300 Subject: [PATCH 3/6] Implement EIP-211 --- .../org/ethereum/config/BlockchainConfig.java | 2 + .../config/blockchain/AbstractConfig.java | 5 ++ .../config/blockchain/ByzantiumConfig.java | 5 ++ .../config/blockchain/Eip150HFConfig.java | 5 ++ .../src/main/java/org/ethereum/vm/OpCode.java | 4 ++ .../src/main/java/org/ethereum/vm/VM.java | 59 +++++++++++++++---- .../java/org/ethereum/vm/program/Program.java | 33 ++++++++++- 7 files changed, 101 insertions(+), 12 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java index fea46125d2..886394aa7e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java @@ -129,4 +129,6 @@ String validateTransactionChanges(BlockStore blockStore, Block curBlock, Transac Integer getChainId(); boolean eip140(); + + boolean eip211(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java index b6386d955f..58610c5ef7 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java @@ -163,6 +163,11 @@ public boolean eip140() { return false; } + @Override + public boolean eip211() { + return false; + } + @Override public String toString() { return getClass().getSimpleName(); diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java index 04ea8e6f3e..439a8858b7 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java @@ -67,4 +67,9 @@ public BigInteger getCalcDifficultyMultiplier(BlockHeader curBlock, BlockHeader public boolean eip140() { return true; } + + @Override + public boolean eip211() { + return true; + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java index 1e541ba3c8..da8cc130d3 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java @@ -151,4 +151,9 @@ public Integer getChainId() { public boolean eip140() { return false; } + + @Override + public boolean eip211() { + return false; + } } 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 1ff33bd266..2df6964d2d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java @@ -188,6 +188,10 @@ public enum OpCode { * environment to memory */ CODECOPY(0x39, 3, 0, VeryLowTier), // [len code_start mem_start CODECOPY] + + RETURNDATASIZE(0x3d, 0, 1, BaseTier), + + RETURNDATACOPY(0x3e, 3, 0, VeryLowTier), /** * (0x3a) Get price of gas in current * environment 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 70a064250e..0c58538c38 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -146,16 +146,26 @@ public void step(Program program) { OpCode op = OpCode.code(program.getCurrentOp()); if (op == null) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); - } else if (op == DELEGATECALL) { - // opcode since Homestead release only - if (!blockchainConfig.getConstants().hasDelegateCallOpcode()) { - throw Program.Exception.invalidOpCode(program.getCurrentOp()); - } - } else if (op == REVERT) { - // opcode since Bizantium HF only - if (!blockchainConfig.eip140()) { - throw Program.Exception.invalidOpCode(program.getCurrentOp()); - } + } + + switch (op) { + case DELEGATECALL: + if (!blockchainConfig.getConstants().hasDelegateCallOpcode()) { + // opcode since Homestead release only + throw Program.Exception.invalidOpCode(program.getCurrentOp()); + } + break; + case REVERT: + if (!blockchainConfig.eip140()) { + throw Program.Exception.invalidOpCode(program.getCurrentOp()); + } + break; + case RETURNDATACOPY: + case RETURNDATASIZE: + if (!blockchainConfig.eip211()) { + throw Program.Exception.invalidOpCode(program.getCurrentOp()); + } + break; } program.setLastOp(op.val()); @@ -247,6 +257,7 @@ else if (oldValue != null && newValue.isZero()) { gasCost += chunkUsed * gasCosts.getSHA3_WORD(); break; case CALLDATACOPY: + case RETURNDATACOPY: gasCost += calcMemGas(gasCosts, oldMemSize, memNeeded(stack.peek(), stack.get(stack.size() - 3)), stack.get(stack.size() - 3).longValueSafe()); @@ -775,6 +786,34 @@ else if (oldValue != null && newValue.isZero()) { program.step(); } break; + case RETURNDATASIZE: { + DataWord dataSize = program.getReturnDataBufferSize(); + + if (logger.isInfoEnabled()) + hint = "size: " + dataSize.value(); + + program.stackPush(dataSize); + program.step(); + } + break; + case RETURNDATACOPY: { + DataWord memOffsetData = program.stackPop(); + DataWord dataOffsetData = program.stackPop(); + DataWord lengthData = program.stackPop(); + + byte[] msgData = program.getReturnDataBufferData(dataOffsetData, lengthData); + + if (msgData == null) { + throw new Program.ReturnDataCopyIllegalBoundsException(dataOffsetData, lengthData, program.getReturnDataBufferSize().longValueSafe()); + } + + if (logger.isInfoEnabled()) + hint = "data: " + Hex.toHexString(msgData); + + program.memorySave(memOffsetData.intValueSafe(), msgData); + program.step(); + } + break; case CODESIZE: case EXTCODESIZE: { diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/program/Program.java b/ethereumj-core/src/main/java/org/ethereum/vm/program/Program.java index f8904becd9..4a05895f70 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -42,7 +42,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spongycastle.util.encoders.Hex; -import org.springframework.beans.factory.annotation.Autowired; import java.io.ByteArrayOutputStream; import java.math.BigInteger; @@ -87,6 +86,7 @@ public class Program { private Stack stack; private Memory memory; private Storage storage; + private byte[] returnDataBuffer; private ProgramResult result = new ProgramResult(); private ProgramTrace trace = new ProgramTrace(); @@ -460,8 +460,9 @@ this, new DataWord(newAddress), getOwnerAddress(), value, gasLimit, newBalance, null, track, this.invoke.getBlockStore(), byTestingSuite()); ProgramResult result = ProgramResult.empty(); - if (isNotEmpty(programCode)) { + returnDataBuffer = null; // reset return buffer right before the call + if (isNotEmpty(programCode)) { VM vm = new VM(config); Program program = new Program(programCode, programInvoke, internalTx, config).withCommonConfig(commonConfig); vm.play(program); @@ -483,6 +484,8 @@ this, new DataWord(newAddress), getOwnerAddress(), value, gasLimit, if (result.getException() != null) { return; + } else { + returnDataBuffer = result.getHReturn(); } } else { // 4. CREATE THE CONTRACT OUT OF RETURN @@ -583,6 +586,8 @@ public void callToAddress(MessageCall msg) { ProgramResult result = null; if (isNotEmpty(programCode)) { + returnDataBuffer = null; // reset return buffer right before the call + ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke( this, new DataWord(contextAddress), msg.getType() == MsgType.DELEGATECALL ? getCallerAddress() : getOwnerAddress(), @@ -635,6 +640,8 @@ this, new DataWord(contextAddress), int size = msg.getOutDataSize().intValue(); memorySaveLimited(offset, buffer, size); + + returnDataBuffer = buffer; } // 5. REFUND THE REMAIN GAS @@ -751,6 +758,20 @@ public byte[] getDataCopy(DataWord offset, DataWord length) { return invoke.getDataCopy(offset, length); } + public DataWord getReturnDataBufferSize() { + return new DataWord(getReturnDataBufferSizeI()); + } + + private int getReturnDataBufferSizeI() { + return returnDataBuffer == null ? 0 : returnDataBuffer.length; + } + + public byte[] getReturnDataBufferData(DataWord off, DataWord size) { + if ((long) off.intValueSafe() + size.intValueSafe() > getReturnDataBufferSizeI()) return null; + return returnDataBuffer == null ? new byte[0] : + Arrays.copyOfRange(returnDataBuffer, off.intValueSafe(), off.intValueSafe() + size.intValueSafe()); + } + public DataWord storageLoad(DataWord key) { DataWord ret = getStorage().getStorageValue(getOwnerAddress().getLast20Bytes(), key.clone()); return ret == null ? null : ret.clone(); @@ -1206,6 +1227,14 @@ public StackTooSmallException(String message, Object... args) { } } + @SuppressWarnings("serial") + public static class ReturnDataCopyIllegalBoundsException extends BytecodeExecutionException { + public ReturnDataCopyIllegalBoundsException(DataWord off, DataWord size, long returnDataSize) { + super(String.format("Illegal RETURNDATACOPY arguments: offset (%s) + size (%s) > RETURNDATASIZE (%d)", off, size, returnDataSize)); + } + } + + public static class Exception { public static OutOfGasException notEnoughOpGas(OpCode op, long opGas, long programGas) { From cabd2c0ea6241ac9c879d8342d67cfa2723775ca Mon Sep 17 00:00:00 2001 From: Anton Nahsatyrev Date: Wed, 16 Aug 2017 18:25:20 +0300 Subject: [PATCH 4/6] Rename EIP140 -> EIP206 --- .../src/main/java/org/ethereum/config/BlockchainConfig.java | 5 ++++- .../java/org/ethereum/config/blockchain/AbstractConfig.java | 2 +- .../java/org/ethereum/config/blockchain/ByzantiumConfig.java | 2 +- .../java/org/ethereum/config/blockchain/Eip150HFConfig.java | 2 +- ethereumj-core/src/main/java/org/ethereum/vm/VM.java | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java index fea46125d2..7bc1491c9a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java @@ -128,5 +128,8 @@ String validateTransactionChanges(BlockStore blockStore, Block curBlock, Transac */ Integer getChainId(); - boolean eip140(); + /** + * EIP206: https://github.com/ethereum/EIPs/pull/206 + */ + boolean eip206(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java index b6386d955f..635fa5503f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java @@ -159,7 +159,7 @@ public Integer getChainId() { } @Override - public boolean eip140() { + public boolean eip206() { return false; } diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java index 04ea8e6f3e..3eb424ad7e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/ByzantiumConfig.java @@ -64,7 +64,7 @@ public BigInteger getCalcDifficultyMultiplier(BlockHeader curBlock, BlockHeader } @Override - public boolean eip140() { + public boolean eip206() { return true; } } diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java index 1e541ba3c8..7e2e347c1d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/Eip150HFConfig.java @@ -148,7 +148,7 @@ public Integer getChainId() { } @Override - public boolean eip140() { + public boolean eip206() { return false; } } 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 70a064250e..f1ac0858e1 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -153,7 +153,7 @@ public void step(Program program) { } } else if (op == REVERT) { // opcode since Bizantium HF only - if (!blockchainConfig.eip140()) { + if (!blockchainConfig.eip206()) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); } } From 7fd3a367206173882d7388744f124766af94df68 Mon Sep 17 00:00:00 2001 From: Anton Nahsatyrev Date: Wed, 16 Aug 2017 19:05:09 +0300 Subject: [PATCH 5/6] Fix merge error --- ethereumj-core/src/main/java/org/ethereum/vm/VM.java | 10 ---------- 1 file changed, 10 deletions(-) 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 eaba1df348..a84ac62a2d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -146,16 +146,6 @@ public void step(Program program) { OpCode op = OpCode.code(program.getCurrentOp()); if (op == null) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); - } else if (op == DELEGATECALL) { - // opcode since Homestead release only - if (!blockchainConfig.getConstants().hasDelegateCallOpcode()) { - throw Program.Exception.invalidOpCode(program.getCurrentOp()); - } - } else if (op == REVERT) { - // opcode since Bizantium HF only - if (!blockchainConfig.eip140()) { - throw Program.Exception.invalidOpCode(program.getCurrentOp()); - } } switch (op) { From c4054c55bba55aa6406407c06aad2269d8090f46 Mon Sep 17 00:00:00 2001 From: Anton Nahsatyrev Date: Wed, 16 Aug 2017 19:07:19 +0300 Subject: [PATCH 6/6] Add eip link --- .../src/main/java/org/ethereum/config/BlockchainConfig.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java index 13625c2a7d..25da34b505 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java @@ -133,5 +133,8 @@ String validateTransactionChanges(BlockStore blockStore, Block curBlock, Transac */ boolean eip206(); + /** + * EIP211: https://github.com/ethereum/EIPs/pull/211 + */ boolean eip211(); }