Skip to content

Commit

Permalink
Merge pull request #1162 from ethereum/feature/eip-1052
Browse files Browse the repository at this point in the history
Feature/eip 1052
  • Loading branch information
mkalinin authored Aug 23, 2018
2 parents fa26723 + 6f26891 commit 82d96c1
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,10 @@ String validateTransactionChanges(BlockStore blockStore, Block curBlock, Transac
* Replaces the intermediate state root field of the receipt with the status
*/
boolean eip658();

/**
* EIP1052: https://eips.ethereum.org/EIPS/eip-1052
* EXTCODEHASH opcode
*/
boolean eip1052();
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ public boolean eip658() {
return false;
}

@Override
public boolean eip1052() {
return false;
}

@Override
public String toString() {
return getClass().getSimpleName();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) [2016] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.config.blockchain;

import org.ethereum.config.BlockchainConfig;

/**
* EIPs included in the Constantinople Hard Fork:
* <ul>
* <li>145 - Bitwise shifting instructions in EVM</li>
* <li>1014 - Skinny CREATE2</li>
* <li>1052 - EXTCODEHASH opcode</li>
* <li>1087 - Net gas metering for SSTORE operations</li>
* </ul>
*/
public class ConstantinopleConfig extends ByzantiumConfig {

public ConstantinopleConfig(BlockchainConfig parent) {
super(parent);
}

@Override
public boolean eip1052() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,9 @@ public boolean eip214() {
public boolean eip658() {
return false;
}

@Override
public boolean eip1052() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public synchronized void saveCode(byte[] addr, byte[] code) {
@Override
public synchronized byte[] getCode(byte[] addr) {
byte[] codeHash = getCodeHash(addr);
return FastByteComparisons.equal(codeHash, HashUtil.EMPTY_DATA_HASH) ?
return codeHash == null || FastByteComparisons.equal(codeHash, HashUtil.EMPTY_DATA_HASH) ?
ByteUtil.EMPTY_BYTE_ARRAY : codeCache.get(codeKey(codeHash, addr));
}

Expand All @@ -147,7 +147,7 @@ private byte[] codeKey(byte[] codeHash, byte[] addr) {
@Override
public byte[] getCodeHash(byte[] addr) {
AccountState accountState = getAccountState(addr);
return accountState != null ? accountState.getCodeHash() : HashUtil.EMPTY_DATA_HASH;
return accountState != null ? accountState.getCodeHash() : null;
}

@Override
Expand Down
5 changes: 5 additions & 0 deletions ethereumj-core/src/main/java/org/ethereum/vm/GasCost.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public class GasCost {
private final int EC_RECOVER = 3000;
private final int EXT_CODE_SIZE = 20;
private final int EXT_CODE_COPY = 20;
private final int EXT_CODE_HASH = 400;
private final int NEW_ACCT_SUICIDE = 0;

public int getSTEP() {
Expand Down Expand Up @@ -291,4 +292,8 @@ public int getEXT_CODE_SIZE() {
public int getEXT_CODE_COPY() {
return EXT_CODE_COPY;
}

public int getEXT_CODE_HASH() {
return EXT_CODE_HASH;
}
}
5 changes: 5 additions & 0 deletions ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ public enum OpCode {
* environment to memory with given offset
*/
EXTCODECOPY(0x3c, 4, 0, ExtTier),
/**
* (0x3f) Returns the keccak256 hash of
* a contract’s code
*/
EXTCODEHASH(0x3f, 1,1 , ExtTier),

/* Block Information */

Expand Down
66 changes: 41 additions & 25 deletions ethereumj-core/src/main/java/org/ethereum/vm/VM.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;

import static org.ethereum.crypto.HashUtil.EMPTY_DATA_HASH;
import static org.ethereum.crypto.HashUtil.sha3;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.ethereum.vm.OpCode.*;
Expand Down Expand Up @@ -95,6 +99,16 @@ public class VM {
private boolean vmTrace;
private long dumpBlock;

private static final Map<OpCode, Function<BlockchainConfig, Boolean>> opValidators = new HashMap<OpCode, Function<BlockchainConfig, Boolean>>()
{{
put(DELEGATECALL, (config) -> config.getConstants().hasDelegateCallOpcode());
put(REVERT, BlockchainConfig::eip206);
put(RETURNDATACOPY, BlockchainConfig::eip211);
put(RETURNDATASIZE, BlockchainConfig::eip211);
put(STATICCALL, BlockchainConfig::eip214);
put(EXTCODEHASH, BlockchainConfig::eip1052);
}};

private final SystemProperties config;

public VM() {
Expand Down Expand Up @@ -146,6 +160,21 @@ private boolean isDeadAccount(Program program, byte[] addr) {
return !program.getStorage().isExist(addr) || program.getStorage().getAccountState(addr).isEmpty();
}

/**
* Validates whether operation is allowed
* with current blockchain config
* @param op VM operation
* @param program Current program
*/
private void validateOp(OpCode op, Program program) {
if (!(opValidators.containsKey(op))) return;

BlockchainConfig blockchainConfig = program.getBlockchainConfig();
if (!opValidators.get(op).apply(blockchainConfig)) {
throw Program.Exception.invalidOpCode(program.getCurrentOp());
}
}

public void step(Program program) {

if (vmTrace) {
Expand All @@ -160,30 +189,7 @@ public void step(Program program) {
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.eip206()) {
throw Program.Exception.invalidOpCode(program.getCurrentOp());
}
break;
case RETURNDATACOPY:
case RETURNDATASIZE:
if (!blockchainConfig.eip211()) {
throw Program.Exception.invalidOpCode(program.getCurrentOp());
}
break;
case STATICCALL:
if (!blockchainConfig.eip214()) {
throw Program.Exception.invalidOpCode(program.getCurrentOp());
}
break;
}
validateOp(op, program);

program.setLastOp(op.val());
program.verifyStackSize(op.require());
Expand Down Expand Up @@ -292,6 +298,9 @@ else if (oldValue != null && newValue.isZero()) {
memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 4)),
stack.get(stack.size() - 4).longValueSafe());
break;
case EXTCODEHASH:
gasCost = gasCosts.getEXT_CODE_HASH();
break;
case CALL:
case CALLCODE:
case DELEGATECALL:
Expand Down Expand Up @@ -871,6 +880,13 @@ else if (oldValue != null && newValue.isZero()) {
program.step();
}
break;
case EXTCODEHASH: {
DataWord address = program.stackPop();
byte[] codeHash = program.getCodeHashAt(address);
program.stackPush(codeHash);
program.step();
}
break;
case GASPRICE: {
DataWord gasPrice = program.getGasPrice();

Expand Down Expand Up @@ -1314,7 +1330,7 @@ public void play(Program program) {
} catch (RuntimeException e) {
program.setRuntimeFailure(e);
} catch (StackOverflowError soe) {
logger.error("\n !!! StackOverflowError: update your java run command with -Xss2M (-Xss4M for tests) !!!\n", soe);
logger.error("\n !!! StackOverflowError: update your java run command with -Xss2M (-Xss8M for tests) !!!\n", soe);
System.exit(-1);
} finally {
callVmHookAction(program, VMHook::stopPlay);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,11 @@ public byte[] getCodeAt(DataWord address) {
return nullToEmpty(code);
}

public byte[] getCodeHashAt(DataWord address) {
byte[] code = invoke.getRepository().getCodeHash(address.getLast20Bytes());
return nullToEmpty(code);
}

public DataWord getOwnerAddress() {
return invoke.getOwnerAddress();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,22 @@ public void stNonZeroCallsTest() throws IOException {
public void stCodeCopyTest() throws IOException {
suite.runAll("stCodeCopyTest");
}

@Test
@Ignore("Broken tests format, delayed until resolved")
public void stExtCodeHashCallCode() throws IOException {
String commit = "10ab37c095bb87d2e781bcf112b6104912fccb44";
String filePath = "GeneralStateTests/stExtCodeHash/extCodeHashCallCode_d0g0v0.json";
BlockchainTestSuite.runSingle(filePath, commit, GitHubJSONTestSuite.Network.Constantinople);
BlockchainTestSuite.runSingle(filePath, commit, GitHubJSONTestSuite.Network.Byzantium);
}

@Test
@Ignore("Broken tests format, delayed until resolved")
public void stExtCodeHashCall() throws IOException {
String commit = "10ab37c095bb87d2e781bcf112b6104912fccb44";
String filePath = "GeneralStateTests/stExtCodeHash/extCodeHashCall_d0g0v0.json";
BlockchainTestSuite.runSingle(filePath, commit, GitHubJSONTestSuite.Network.Constantinople);
BlockchainTestSuite.runSingle(filePath, commit, GitHubJSONTestSuite.Network.Byzantium);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ public BlockchainNetConfig getConfig() {
case EIP150: return new Eip150HFConfig(new DaoHFConfig());
case EIP158: return new Eip160HFConfig(new DaoHFConfig());
case Byzantium: return new ByzantiumConfig(new DaoHFConfig());
case Constantinople: return new ConstantinopleConfig(new DaoHFConfig());

case FrontierToHomesteadAt5: return new BaseNetConfig() {{
add(0, new FrontierConfig());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,5 +301,24 @@ public void stNonZeroCallsTest() throws IOException {
public void stCodeCopyTest() throws IOException {
suite.runAll("stCodeCopyTest");
}


@Test
@Ignore("Fails on pre-configured commit, update after test is merged in develop of Github tests")
public void stExtCodeHashCallCode() throws IOException {
String commit = "10ab37c095bb87d2e781bcf112b6104912fccb44";
String filePath = "stExtCodeHash/extCodeHashCallCode.json";
GeneralStateTestSuite.runSingle(filePath, commit, GitHubJSONTestSuite.Network.Constantinople);
GeneralStateTestSuite.runSingle(filePath, commit, GitHubJSONTestSuite.Network.Byzantium);
}

@Test
@Ignore("Fails on pre-configured commit, update after test is merged in develop of Github tests")
public void stExtCodeHashCall() throws IOException {
String commit = "10ab37c095bb87d2e781bcf112b6104912fccb44";
String filePath = "stExtCodeHash/extCodeHashCall.json";
GeneralStateTestSuite.runSingle(filePath, commit, GitHubJSONTestSuite.Network.Constantinople);
GeneralStateTestSuite.runSingle(filePath, commit, GitHubJSONTestSuite.Network.Byzantium);
}
}

0 comments on commit 82d96c1

Please sign in to comment.