Enforce EOF version validation rules (#4959)

Add enforcement of the rule that EOF cannot create prior versions or
legacy EVM code contracts in create opcodes and create transactions.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
pull/4969/head
Danno Ferrin 2 years ago committed by GitHub
parent 5627ee03fe
commit db10bebd57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  2. 7
      evm/src/main/java/org/hyperledger/besu/evm/Code.java
  3. 11
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  4. 5
      evm/src/main/java/org/hyperledger/besu/evm/code/CodeInvalid.java
  5. 5
      evm/src/main/java/org/hyperledger/besu/evm/code/CodeV0.java
  6. 5
      evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java
  7. 4
      evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java
  8. 4
      evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/ContractValidationRule.java
  9. 87
      evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java
  10. 4
      evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/MaxCodeSizeRule.java
  11. 4
      evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java
  12. 8
      evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java
  13. 2
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java
  14. 2
      evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java
  15. 57
      evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java
  16. 72
      evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java
  17. 136
      evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java

@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.contractvalidation.EOFValidationCodeRule;
import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule;
import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule;
import org.hyperledger.besu.evm.frame.MessageFrame;
@ -716,6 +717,9 @@ public abstract class MainnetProtocolSpecs {
final boolean quorumCompatibilityMode,
final EvmConfiguration evmConfiguration) {
final int contractSizeLimit =
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
return shanghaiDefinition(
chainId,
configContractSizeLimit,
@ -729,6 +733,17 @@ public abstract class MainnetProtocolSpecs {
(gasCalculator, jdCacheConfig) ->
MainnetEVMs.cancun(
gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration))
// change contract call creator to accept EOF code
.contractCreationProcessorBuilder(
(gasCalculator, evm) ->
new ContractCreationProcessor(
gasCalculator,
evm,
true,
List.of(
MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)),
1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
.name("Cancun");
}

@ -75,4 +75,11 @@ public interface Code {
* @return 1 for legacy, count for valid, zero for invalid.
*/
int getCodeSectionCount();
/**
* Returns the EOF version of the code. Legacy code is version 0, invalid code -1.
*
* @return The version of hte ode.
*/
int getEofVersion();
}

@ -723,6 +723,17 @@ public class MainnetEVMs {
registry.put(new Create2Operation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT));
}
/**
* Cancun evm.
*
* @param chainId the chain id
* @param evmConfiguration the evm configuration
* @return the evm
*/
public static EVM cancun(final BigInteger chainId, final EvmConfiguration evmConfiguration) {
return cancun(new ShanghaiGasCalculator(), chainId, evmConfiguration);
}
/**
* Cancun evm.
*

@ -88,4 +88,9 @@ public class CodeInvalid implements Code {
public int getCodeSectionCount() {
return 0;
}
@Override
public int getEofVersion() {
return -1;
}
}

@ -133,6 +133,11 @@ public class CodeV0 implements Code {
return 1;
}
@Override
public int getEofVersion() {
return 0;
}
/**
* Calculate jump destination.
*

@ -79,6 +79,11 @@ public class CodeV1 implements Code {
return true;
}
@Override
public int getEofVersion() {
return eofLayout.getVersion();
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;

@ -21,6 +21,7 @@ import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
@ -41,7 +42,8 @@ public class CachedInvalidCodeRule implements ContractValidationRule {
}
@Override
public Optional<ExceptionalHaltReason> validate(final Bytes contractCode) {
public Optional<ExceptionalHaltReason> validate(
final Bytes contractCode, final MessageFrame frame) {
final Code code =
CodeFactory.createCode(contractCode, Hash.hash(contractCode), maxEofVersion, false);
if (!code.isValid()) {

@ -15,6 +15,7 @@
package org.hyperledger.besu.evm.contractvalidation;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
@ -28,7 +29,8 @@ public interface ContractValidationRule {
* Validate.
*
* @param contractCode the contract code to validate
* @param frame the message frame to use for context
* @return the optional halt reason
*/
Optional<ExceptionalHaltReason> validate(Bytes contractCode);
Optional<ExceptionalHaltReason> validate(Bytes contractCode, MessageFrame frame);
}

@ -0,0 +1,87 @@
/*
* Copyright contributors to Hyperledger Besu
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.contractvalidation;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.code.CodeInvalid;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Validates code is properly EOF formatted if it has the correct magic bytes. This supplants
* PrefixRule.
*/
public class EOFValidationCodeRule implements ContractValidationRule {
private static final Logger LOG = LoggerFactory.getLogger(EOFValidationCodeRule.class);
final int maxEofVersion;
final boolean inCreateTransaction;
private EOFValidationCodeRule(final int maxEofVersion, final boolean inCreateTransaction) {
this.maxEofVersion = maxEofVersion;
this.inCreateTransaction = inCreateTransaction;
}
/**
* Runs eof validation rules as per the various EIPs covering it.
*
* @param contractCode the contract code to validate
* @param frame the message frame to use for context
* @return Either an empty optional on success, or an optional containing one of the invalid
* reasons.
*/
@Override
public Optional<ExceptionalHaltReason> validate(
final Bytes contractCode, final MessageFrame frame) {
Code code =
CodeFactory.createCode(
contractCode, Hash.hash(contractCode), maxEofVersion, inCreateTransaction);
if (!code.isValid()) {
LOG.trace("EOF Validation Error: {}", ((CodeInvalid) code).getInvalidReason());
return Optional.of(ExceptionalHaltReason.INVALID_CODE);
}
if (frame.getCode().getEofVersion() > code.getEofVersion()) {
LOG.trace(
"Cannot deploy older eof versions: initcode version - {} runtime code version - {}",
frame.getCode().getEofVersion(),
code.getEofVersion());
return Optional.of(ExceptionalHaltReason.EOF_CREATE_VERSION_INCOMPATIBLE);
}
return Optional.empty();
}
/**
* Create EOF validation.
*
* @param maxEofVersion Maximum EOF version to validate
* @param inCreateTransaction Is this inside a create transaction?
* @return The EOF validation contract validation rule.
*/
public static ContractValidationRule of(
final int maxEofVersion, final boolean inCreateTransaction) {
return new EOFValidationCodeRule(maxEofVersion, inCreateTransaction);
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.evm.contractvalidation;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
@ -39,7 +40,8 @@ public class MaxCodeSizeRule implements ContractValidationRule {
}
@Override
public Optional<ExceptionalHaltReason> validate(final Bytes contractCode) {
public Optional<ExceptionalHaltReason> validate(
final Bytes contractCode, final MessageFrame frame) {
final int contractCodeSize = contractCode.size();
if (contractCodeSize <= maxCodeSize) {
return Optional.empty();

@ -15,6 +15,7 @@
package org.hyperledger.besu.evm.contractvalidation;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
@ -31,7 +32,8 @@ public class PrefixCodeRule implements ContractValidationRule {
@Override
// As per https://eips.ethereum.org/EIPS/eip-3541
public Optional<ExceptionalHaltReason> validate(final Bytes contractCode) {
public Optional<ExceptionalHaltReason> validate(
final Bytes contractCode, final MessageFrame frame) {
if (!contractCode.isEmpty() && contractCode.get(0) == FORMAT_RESERVED) {
LOG.trace("Contract creation error: code cannot start with {}", FORMAT_RESERVED);
return Optional.of(ExceptionalHaltReason.INVALID_CODE);

@ -55,6 +55,9 @@ public interface ExceptionalHaltReason {
DefaultExceptionalHaltReason.TOO_FEW_INPUTS_FOR_CODE_SECTION;
/** The constant JUMPF_STACK_MISMATCH. */
ExceptionalHaltReason JUMPF_STACK_MISMATCH = DefaultExceptionalHaltReason.JUMPF_STACK_MISMATCH;
/** The constant EOF_CREATE_VERSION_INCOMPATIBLE. */
ExceptionalHaltReason EOF_CREATE_VERSION_INCOMPATIBLE =
DefaultExceptionalHaltReason.EOF_CREATE_VERSION_INCOMPATIBLE;
/**
* Name string.
@ -107,7 +110,10 @@ public interface ExceptionalHaltReason {
TOO_FEW_INPUTS_FOR_CODE_SECTION("Not enough stack items for a function call"),
/** The Jumpf stack mismatch. */
JUMPF_STACK_MISMATCH(
"The stack height for a JUMPF does not match the requirements of the target section");
"The stack height for a JUMPF does not match the requirements of the target section"),
/** The Eof version incompatible. */
EOF_CREATE_VERSION_INCOMPATIBLE(
"EOF Code is attempting to create EOF code of an earlier version");
/** The Description. */
final String description;

@ -98,7 +98,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
final Bytes inputData = frame.readMemory(inputOffset, inputSize);
Code code = evm.getCode(Hash.hash(inputData), inputData);
if (code.isValid()) {
if (code.isValid() && frame.getCode().getEofVersion() <= code.getEofVersion()) {
frame.decrementRemainingGas(cost);
spawnChildMessage(frame, code, evm);
frame.incrementRemainingGas(cost);

@ -159,7 +159,7 @@ public class ContractCreationProcessor extends AbstractMessageProcessor {
} else {
final var invalidReason =
contractValidationRules.stream()
.map(rule -> rule.validate(contractCode))
.map(rule -> rule.validate(contractCode, frame))
.filter(Optional::isPresent)
.findFirst();
if (invalidReason.isEmpty()) {

@ -36,6 +36,7 @@ import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.Create2Operation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount;
@ -81,6 +82,8 @@ public class Create2OperationTest {
+ "6000" // PUSH1 0x00
+ "F3" // RETURN
);
public static final Bytes SIMPLE_EOF =
Bytes.fromHexString("0xEF00010100040200010001030000000000000000");
public static final String SENDER = "0xdeadc0de00000000000000000000000000000000";
private static final int SHANGHAI_CREATE_GAS = 41240 + (0xc000 / 32) * 6;
@ -302,4 +305,58 @@ public class Create2OperationTest {
memoryOffset.trimLeadingZeros().toInt(), SIMPLE_CREATE.size(), SIMPLE_CREATE);
return messageFrame;
}
@Test
void eofV1CannotCreateLegacy() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
final MessageFrame messageFrame =
new TestMessageFrameBuilder()
.code(CodeFactory.createCode(SIMPLE_EOF, Hash.hash(SIMPLE_EOF), 1, true))
.pushStackItem(Bytes.EMPTY)
.pushStackItem(memoryLength)
.pushStackItem(memoryOffset)
.pushStackItem(Bytes.EMPTY)
.worldUpdater(worldUpdater)
.build();
messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_CREATE);
when(account.getMutable()).thenReturn(mutableAccount);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm);
assertThat(result.getHaltReason()).isNull();
assertThat(messageFrame.getStackItem(0)).isEqualTo(UInt256.ZERO);
}
@Test
void legacyCanCreateEOFv1() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_EOF.size());
final MessageFrame messageFrame =
new TestMessageFrameBuilder()
.code(CodeFactory.createCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE), 1, true))
.pushStackItem(Bytes.EMPTY)
.pushStackItem(memoryLength)
.pushStackItem(memoryOffset)
.pushStackItem(Bytes.EMPTY)
.worldUpdater(worldUpdater)
.build();
messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_EOF);
when(account.getMutable()).thenReturn(mutableAccount);
when(account.getNonce()).thenReturn(55L);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
when(worldUpdater.get(any())).thenReturn(account);
when(worldUpdater.getSenderAccount(any())).thenReturn(account);
when(worldUpdater.updater()).thenReturn(worldUpdater);
final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm);
assertThat(result.getHaltReason()).isNull();
assertThat(messageFrame.getStackItem(0)).isNotEqualTo(UInt256.ZERO);
}
}

@ -36,6 +36,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.CreateOperation;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount;
@ -46,9 +47,9 @@ import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.junit.jupiter.api.Test;
public class CreateOperationTest {
class CreateOperationTest {
private final WorldUpdater worldUpdater = mock(WorldUpdater.class);
private final WrappedEvmAccount account = mock(WrappedEvmAccount.class);
@ -75,11 +76,14 @@ public class CreateOperationTest {
+ "6000" // PUSH1 0x00
+ "F3" // RETURN
);
public static final Bytes SIMPLE_EOF =
Bytes.fromHexString("0xEF00010100040200010001030000000000000000");
public static final String SENDER = "0xdeadc0de00000000000000000000000000000000";
private static final int SHANGHAI_CREATE_GAS = 41240;
@Test
public void createFromMemoryMutationSafe() {
void createFromMemoryMutationSafe() {
// Given: Execute a CREATE operation with a contract that logs in the constructor
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
@ -122,7 +126,7 @@ public class CreateOperationTest {
}
@Test
public void nonceTooLarge() {
void nonceTooLarge() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
@ -141,7 +145,7 @@ public class CreateOperationTest {
}
@Test
public void messageFrameStackTooDeep() {
void messageFrameStackTooDeep() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
@ -160,7 +164,7 @@ public class CreateOperationTest {
}
@Test
public void notEnoughValue() {
void notEnoughValue() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
@ -182,7 +186,7 @@ public class CreateOperationTest {
}
@Test
public void shanghaiMaxInitCodeSizeCreate() {
void shanghaiMaxInitCodeSizeCreate() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.fromHexString("0xc000");
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
@ -214,7 +218,7 @@ public class CreateOperationTest {
}
@Test
public void shanghaiMaxInitCodeSizePlus1Create() {
void shanghaiMaxInitCodeSizePlus1Create() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.fromHexString("0xc001");
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
@ -237,6 +241,58 @@ public class CreateOperationTest {
assertThat(result.getHaltReason()).isEqualTo(CODE_TOO_LARGE);
}
@Test
void eofV1CannotCreateLegacy() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
final MessageFrame messageFrame =
new TestMessageFrameBuilder()
.code(CodeFactory.createCode(SIMPLE_EOF, Hash.hash(SIMPLE_EOF), 1, true))
.pushStackItem(memoryLength)
.pushStackItem(memoryOffset)
.pushStackItem(Bytes.EMPTY)
.worldUpdater(worldUpdater)
.build();
messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_CREATE);
when(account.getMutable()).thenReturn(mutableAccount);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm);
assertThat(result.getHaltReason()).isNull();
assertThat(messageFrame.getStackItem(0)).isEqualTo(UInt256.ZERO);
}
@Test
void legacyCanCreateEOFv1() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_EOF.size());
final MessageFrame messageFrame =
new TestMessageFrameBuilder()
.code(CodeFactory.createCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE), 1, true))
.pushStackItem(memoryLength)
.pushStackItem(memoryOffset)
.pushStackItem(Bytes.EMPTY)
.worldUpdater(worldUpdater)
.build();
messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_EOF);
when(account.getMutable()).thenReturn(mutableAccount);
when(account.getNonce()).thenReturn(55L);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
when(worldUpdater.get(any())).thenReturn(account);
when(worldUpdater.getSenderAccount(any())).thenReturn(account);
when(worldUpdater.updater()).thenReturn(worldUpdater);
final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm);
assertThat(result.getHaltReason()).isNull();
assertThat(messageFrame.getStackItem(0)).isNotEqualTo(UInt256.ZERO);
}
@NotNull
private MessageFrame testMemoryFrame(
final UInt256 memoryOffset,

@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet;
package org.hyperledger.besu.evm.processor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.frame.MessageFrame.State.COMPLETED_SUCCESS;
@ -20,17 +20,17 @@ import static org.hyperledger.besu.evm.frame.MessageFrame.State.EXCEPTIONAL_HALT
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.MessageFrameTestFixture;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.contractvalidation.CachedInvalidCodeRule;
import org.hyperledger.besu.evm.contractvalidation.EOFValidationCodeRule;
import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule;
import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import java.util.Collections;
@ -42,7 +42,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class MainnetContractCreationProcessorTest {
public class ContractCreationProcessorTest {
@Mock GasCalculator gasCalculator;
@Mock EVM evm;
@ -50,7 +50,7 @@ public class MainnetContractCreationProcessorTest {
private ContractCreationProcessor processor;
@Test
public void shouldThrowAnExceptionWhenCodeContractFormatInvalid() {
public void shouldThrowAnExceptionWhenCodeContractFormatInvalidPreEOF() {
processor =
new ContractCreationProcessor(
gasCalculator,
@ -60,7 +60,7 @@ public class MainnetContractCreationProcessorTest {
1,
Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("EF01010101010101");
final MessageFrame messageFrame = new MessageFrameTestFixture().build();
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
@ -82,7 +82,7 @@ public class MainnetContractCreationProcessorTest {
1,
Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("0101010101010101");
final MessageFrame messageFrame = new MessageFrameTestFixture().build();
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
@ -97,7 +97,7 @@ public class MainnetContractCreationProcessorTest {
new ContractCreationProcessor(
gasCalculator, evm, true, Collections.emptyList(), 1, Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("0F01010101010101");
final MessageFrame messageFrame = new MessageFrameTestFixture().build();
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
@ -106,6 +106,118 @@ public class MainnetContractCreationProcessorTest {
assertThat(messageFrame.getState()).isEqualTo(COMPLETED_SUCCESS);
}
@Test
public void shouldThrowAnExceptionWhenCodeContractFormatInvalidPostEOF() {
processor =
new ContractCreationProcessor(
gasCalculator,
evm,
true,
Collections.singletonList(EOFValidationCodeRule.of(1, false)),
1,
Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("EF00010101010101");
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
when(gasCalculator.codeDepositGasCost(contractCode.size())).thenReturn(10L);
processor.codeSuccess(messageFrame, OperationTracer.NO_TRACING);
assertThat(messageFrame.getState()).isEqualTo(EXCEPTIONAL_HALT);
assertThat(messageFrame.getExceptionalHaltReason())
.contains(ExceptionalHaltReason.INVALID_CODE);
}
@Test
public void eofValidationShouldAllowLegacyCode() {
processor =
new ContractCreationProcessor(
gasCalculator,
evm,
true,
Collections.singletonList(EOFValidationCodeRule.of(1, false)),
1,
Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("0101010101010101");
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
when(gasCalculator.codeDepositGasCost(contractCode.size())).thenReturn(10L);
processor.codeSuccess(messageFrame, OperationTracer.NO_TRACING);
assertThat(messageFrame.getState()).isEqualTo(COMPLETED_SUCCESS);
}
@Test
public void eofValidationShouldAllowEOFCode() {
processor =
new ContractCreationProcessor(
gasCalculator,
evm,
true,
Collections.singletonList(EOFValidationCodeRule.of(1, false)),
1,
Collections.emptyList());
final Bytes contractCode =
Bytes.fromHexString(
"0xEF000101000C020003000b000200080300000000000002020100020100000260016002b00001b00002b101b160005360106000f3");
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
when(gasCalculator.codeDepositGasCost(contractCode.size())).thenReturn(10L);
processor.codeSuccess(messageFrame, OperationTracer.NO_TRACING);
assertThat(messageFrame.getState()).isEqualTo(COMPLETED_SUCCESS);
}
@Test
public void eofValidationShouldPreventLegacyCodeDeployment() {
processor =
new ContractCreationProcessor(
gasCalculator,
evm,
true,
Collections.singletonList(EOFValidationCodeRule.of(1, false)),
1,
Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("6030602001");
final Bytes initCode =
Bytes.fromHexString(
"0xEF000101000C020003000b000200080300000000000002020100020100000260016002b00001b00002b101b160005360106000f3");
final MessageFrame messageFrame =
new TestMessageFrameBuilder()
.code(CodeFactory.createCode(initCode, Hash.hash(initCode), 1, true))
.build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
when(gasCalculator.codeDepositGasCost(contractCode.size())).thenReturn(10L);
processor.codeSuccess(messageFrame, OperationTracer.NO_TRACING);
assertThat(messageFrame.getState()).isEqualTo(EXCEPTIONAL_HALT);
}
@Test
public void eofValidationPreventsInvalidEOFCode() {
processor =
new ContractCreationProcessor(
gasCalculator,
evm,
true,
Collections.singletonList(EOFValidationCodeRule.of(1, false)),
1,
Collections.emptyList());
final Bytes contractCode =
Bytes.fromHexString(
"0xEF000101000C020003000b000200080300000000000000020100020100000260016002b00001b00002b101b160005360106000f3");
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
when(gasCalculator.codeDepositGasCost(contractCode.size())).thenReturn(10L);
processor.codeSuccess(messageFrame, OperationTracer.NO_TRACING);
assertThat(messageFrame.getState()).isEqualTo(EXCEPTIONAL_HALT);
}
@Test
public void shouldThrowAnExceptionWhenCodeContractTooLarge() {
processor =
@ -117,7 +229,7 @@ public class MainnetContractCreationProcessorTest {
1,
Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("00".repeat(24 * 1024 + 1));
final MessageFrame messageFrame = new MessageFrameTestFixture().build();
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
@ -141,7 +253,7 @@ public class MainnetContractCreationProcessorTest {
Collections.emptyList());
final Bytes contractCreateCode = Bytes.fromHexString("0x67ef0001010001006060005260086018f3");
final MessageFrame messageFrame =
new MessageFrameTestFixture()
new TestMessageFrameBuilder()
.code(
CodeFactory.createCode(
contractCreateCode,
@ -166,7 +278,7 @@ public class MainnetContractCreationProcessorTest {
1,
Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("00".repeat(24 * 1024));
final MessageFrame messageFrame = new MessageFrameTestFixture().build();
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
@ -181,7 +293,7 @@ public class MainnetContractCreationProcessorTest {
new ContractCreationProcessor(
gasCalculator, evm, true, Collections.emptyList(), 1, Collections.emptyList());
final Bytes contractCode = Bytes.fromHexString("00".repeat(24 * 1024 + 1));
final MessageFrame messageFrame = new MessageFrameTestFixture().build();
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
messageFrame.setGasRemaining(100L);
Loading…
Cancel
Save