EIP-3860 Initcode Size Reference Test fixes (#4911)

* EIP-3860 Initcode Size Reference Test fixes

Fix corner cases around initcode size checking in reference tests.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
pull/4906/head
Danno Ferrin 2 years ago committed by GitHub
parent 593e79f77c
commit 80bebf2b73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  2. 17
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  3. 1
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java
  4. 50
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java
  5. 9
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  6. 10
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java
  7. 4
      evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java
  8. 4
      evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java
  9. 181
      evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java
  10. 14
      evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java

@ -36,10 +36,8 @@ import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.MainnetEVMs; import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.contractvalidation.CachedInvalidCodeRule;
import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule; import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule;
import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule; import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
@ -532,7 +530,8 @@ public abstract class MainnetProtocolSpecs {
TransactionType.FRONTIER, TransactionType.FRONTIER,
TransactionType.ACCESS_LIST, TransactionType.ACCESS_LIST,
TransactionType.EIP1559), TransactionType.EIP1559),
quorumCompatibilityMode)) quorumCompatibilityMode,
Integer.MAX_VALUE))
.transactionProcessorBuilder( .transactionProcessorBuilder(
(gasCalculator, (gasCalculator,
transactionValidator, transactionValidator,
@ -655,9 +654,6 @@ public abstract class MainnetProtocolSpecs {
? FeeMarket.zeroBaseFee(londonForkBlockNumber) ? FeeMarket.zeroBaseFee(londonForkBlockNumber)
: FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); : FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas());
// constant for max initcode size for EIP-3860 limit and meter initcode
final int contractSizeLimit = configContractSizeLimit.orElse(SHANGHAI_INIT_CODE_SIZE_LIMIT);
return parisDefinition( return parisDefinition(
chainId, chainId,
configContractSizeLimit, configContractSizeLimit,
@ -690,17 +686,19 @@ public abstract class MainnetProtocolSpecs {
londonFeeMarket, londonFeeMarket,
CoinbaseFeePriceCalculator.eip1559())) CoinbaseFeePriceCalculator.eip1559()))
// Contract creation rules for EIP-3860 Limit and meter intitcode // Contract creation rules for EIP-3860 Limit and meter intitcode
.contractCreationProcessorBuilder( .transactionValidatorBuilder(
(gasCalculator, evm) -> gasCalculator ->
new ContractCreationProcessor( new MainnetTransactionValidator(
gasCalculator, gasCalculator,
evm, londonFeeMarket,
true, true,
List.of( chainId,
MaxCodeSizeRule.of(contractSizeLimit), Set.of(
CachedInvalidCodeRule.of(EvmSpecVersion.SHANGHAI)), TransactionType.FRONTIER,
1, TransactionType.ACCESS_LIST,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) TransactionType.EIP1559),
quorumCompatibilityMode,
SHANGHAI_INIT_CODE_SIZE_LIMIT))
.name("Shanghai"); .name("Shanghai");
} }

@ -53,6 +53,8 @@ public class MainnetTransactionValidator {
private final Set<TransactionType> acceptedTransactionTypes; private final Set<TransactionType> acceptedTransactionTypes;
private final boolean goQuorumCompatibilityMode; private final boolean goQuorumCompatibilityMode;
private final int maxInitcodeSize;
public MainnetTransactionValidator( public MainnetTransactionValidator(
final GasCalculator gasCalculator, final GasCalculator gasCalculator,
final boolean checkSignatureMalleability, final boolean checkSignatureMalleability,
@ -78,7 +80,8 @@ public class MainnetTransactionValidator {
checkSignatureMalleability, checkSignatureMalleability,
chainId, chainId,
acceptedTransactionTypes, acceptedTransactionTypes,
quorumCompatibilityMode); quorumCompatibilityMode,
Integer.MAX_VALUE);
} }
public MainnetTransactionValidator( public MainnetTransactionValidator(
@ -87,13 +90,15 @@ public class MainnetTransactionValidator {
final boolean checkSignatureMalleability, final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId, final Optional<BigInteger> chainId,
final Set<TransactionType> acceptedTransactionTypes, final Set<TransactionType> acceptedTransactionTypes,
final boolean goQuorumCompatibilityMode) { final boolean goQuorumCompatibilityMode,
final int maxInitcodeSize) {
this.gasCalculator = gasCalculator; this.gasCalculator = gasCalculator;
this.feeMarket = feeMarket; this.feeMarket = feeMarket;
this.disallowSignatureMalleability = checkSignatureMalleability; this.disallowSignatureMalleability = checkSignatureMalleability;
this.chainId = chainId; this.chainId = chainId;
this.acceptedTransactionTypes = acceptedTransactionTypes; this.acceptedTransactionTypes = acceptedTransactionTypes;
this.goQuorumCompatibilityMode = goQuorumCompatibilityMode; this.goQuorumCompatibilityMode = goQuorumCompatibilityMode;
this.maxInitcodeSize = maxInitcodeSize;
} }
/** /**
@ -184,6 +189,14 @@ public class MainnetTransactionValidator {
intrinsicGasCost, transaction.getGasLimit())); intrinsicGasCost, transaction.getGasLimit()));
} }
if (transaction.isContractCreation() && transaction.getPayload().size() > maxInitcodeSize) {
return ValidationResult.invalid(
TransactionInvalidReason.INITCODE_TOO_LARGE,
String.format(
"Initcode size of %d exceeds maximum size of %s",
transaction.getPayload().size(), maxInitcodeSize));
}
return ValidationResult.valid(); return ValidationResult.valid();
} }

@ -37,6 +37,7 @@ public enum TransactionInvalidReason {
TRANSACTION_ALREADY_KNOWN, TRANSACTION_ALREADY_KNOWN,
TRANSACTION_REPLACEMENT_UNDERPRICED, TRANSACTION_REPLACEMENT_UNDERPRICED,
MAX_PRIORITY_FEE_PER_GAS_EXCEEDS_MAX_FEE_PER_GAS, MAX_PRIORITY_FEE_PER_GAS_EXCEEDS_MAX_FEE_PER_GAS,
INITCODE_TOO_LARGE,
// Private Transaction Invalid Reasons // Private Transaction Invalid Reasons
PRIVATE_TRANSACTION_FAILED, PRIVATE_TRANSACTION_FAILED,
PRIVATE_NONCE_TOO_LOW, PRIVATE_NONCE_TOO_LOW,

@ -45,8 +45,8 @@ import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.junit.Test; import org.junit.Test;
@ -259,7 +259,8 @@ public class MainnetTransactionValidatorTest {
false, false,
Optional.of(BigInteger.ONE), Optional.of(BigInteger.ONE),
Set.of(TransactionType.values()), Set.of(TransactionType.values()),
defaultGoQuorumCompatibilityMode); defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
validator.setTransactionFilter(transactionFilter(true)); validator.setTransactionFilter(transactionFilter(true));
final Transaction transaction = final Transaction transaction =
@ -342,7 +343,8 @@ public class MainnetTransactionValidatorTest {
false, false,
Optional.of(BigInteger.ONE), Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER), Set.of(TransactionType.FRONTIER),
defaultGoQuorumCompatibilityMode); defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final MainnetTransactionValidator eip1559Validator = final MainnetTransactionValidator eip1559Validator =
new MainnetTransactionValidator( new MainnetTransactionValidator(
@ -351,7 +353,8 @@ public class MainnetTransactionValidatorTest {
false, false,
Optional.of(BigInteger.ONE), Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559), Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode); defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction = final Transaction transaction =
new TransactionTestFixture() new TransactionTestFixture()
@ -383,7 +386,8 @@ public class MainnetTransactionValidatorTest {
false, false,
Optional.of(BigInteger.ONE), Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559), Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode); defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction = final Transaction transaction =
new TransactionTestFixture() new TransactionTestFixture()
.type(TransactionType.EIP1559) .type(TransactionType.EIP1559)
@ -406,7 +410,8 @@ public class MainnetTransactionValidatorTest {
false, false,
Optional.of(BigInteger.ONE), Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559), Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode); defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction = final Transaction transaction =
new TransactionTestFixture() new TransactionTestFixture()
.type(TransactionType.EIP1559) .type(TransactionType.EIP1559)
@ -428,7 +433,8 @@ public class MainnetTransactionValidatorTest {
false, false,
Optional.of(BigInteger.ONE), Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559), Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode); defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction = final Transaction transaction =
new TransactionTestFixture() new TransactionTestFixture()
.maxPriorityFeePerGas(Optional.of(Wei.of(1))) .maxPriorityFeePerGas(Optional.of(Wei.of(1)))
@ -452,7 +458,8 @@ public class MainnetTransactionValidatorTest {
false, false,
Optional.of(BigInteger.ONE), Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559), Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode); defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction = final Transaction transaction =
new TransactionTestFixture() new TransactionTestFixture()
.maxPriorityFeePerGas(Optional.of(Wei.of(1))) .maxPriorityFeePerGas(Optional.of(Wei.of(1)))
@ -468,6 +475,33 @@ public class MainnetTransactionValidatorTest {
.isEqualTo(ValidationResult.valid()); .isEqualTo(ValidationResult.valid());
} }
@Test
public void shouldRejectTooLargeInitcode() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode,
0xc000);
var bigPayload =
new TransactionTestFixture()
.payload(Bytes.fromHexString("0x" + "00".repeat(0xc001)))
.chainId(Optional.of(BigInteger.ONE))
.createTransaction(senderKeys);
var validationResult =
validator.validate(bigPayload, Optional.empty(), transactionValidationParams);
assertThat(validationResult.isValid()).isFalse();
assertThat(validationResult.getInvalidReason())
.isEqualTo(TransactionInvalidReason.INITCODE_TOO_LARGE);
assertThat(validationResult.getErrorMessage())
.isEqualTo("Initcode size of 49153 exceeds maximum size of 49152");
}
@Test @Test
public void goQuorumCompatibilityModeRejectNonZeroGasPrice() { public void goQuorumCompatibilityModeRejectNonZeroGasPrice() {
final MainnetTransactionValidator validator = final MainnetTransactionValidator validator =

@ -123,6 +123,9 @@ public class MainnetEVMs {
public static final BigInteger DEV_NET_CHAIN_ID = BigInteger.valueOf(1337); public static final BigInteger DEV_NET_CHAIN_ID = BigInteger.valueOf(1337);
public static final int SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT = 0x6000;
public static final int SHANGHAI_INIT_CODE_SIZE_LIMIT = 2 * SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT;
private MainnetEVMs() { private MainnetEVMs() {
// utility class // utility class
} }
@ -209,7 +212,7 @@ public class MainnetEVMs {
registry.put(new InvalidOperation(gasCalculator)); registry.put(new InvalidOperation(gasCalculator));
registry.put(new StopOperation(gasCalculator)); registry.put(new StopOperation(gasCalculator));
registry.put(new SelfDestructOperation(gasCalculator)); registry.put(new SelfDestructOperation(gasCalculator));
registry.put(new CreateOperation(gasCalculator)); registry.put(new CreateOperation(gasCalculator, Integer.MAX_VALUE));
registry.put(new CallOperation(gasCalculator)); registry.put(new CallOperation(gasCalculator));
registry.put(new CallCodeOperation(gasCalculator)); registry.put(new CallCodeOperation(gasCalculator));
@ -317,7 +320,7 @@ public class MainnetEVMs {
public static void registerConstantinopleOperations( public static void registerConstantinopleOperations(
final OperationRegistry registry, final GasCalculator gasCalculator) { final OperationRegistry registry, final GasCalculator gasCalculator) {
registerByzantiumOperations(registry, gasCalculator); registerByzantiumOperations(registry, gasCalculator);
registry.put(new Create2Operation(gasCalculator)); registry.put(new Create2Operation(gasCalculator, Integer.MAX_VALUE));
registry.put(new SarOperation(gasCalculator)); registry.put(new SarOperation(gasCalculator));
registry.put(new ShlOperation(gasCalculator)); registry.put(new ShlOperation(gasCalculator));
registry.put(new ShrOperation(gasCalculator)); registry.put(new ShrOperation(gasCalculator));
@ -461,6 +464,8 @@ public class MainnetEVMs {
final BigInteger chainID) { final BigInteger chainID) {
registerParisOperations(registry, gasCalculator, chainID); registerParisOperations(registry, gasCalculator, chainID);
registry.put(new Push0Operation(gasCalculator)); registry.put(new Push0Operation(gasCalculator));
registry.put(new CreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT));
registry.put(new Create2Operation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT));
} }
public static EVM cancun( public static EVM cancun(

@ -36,13 +36,17 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
protected static final OperationResult UNDERFLOW_RESPONSE = protected static final OperationResult UNDERFLOW_RESPONSE =
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS); new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
protected int maxInitcodeSize;
protected AbstractCreateOperation( protected AbstractCreateOperation(
final int opcode, final int opcode,
final String name, final String name,
final int stackItemsConsumed, final int stackItemsConsumed,
final int stackItemsProduced, final int stackItemsProduced,
final GasCalculator gasCalculator) { final GasCalculator gasCalculator,
final int maxInitcodeSize) {
super(opcode, name, stackItemsConsumed, stackItemsProduced, gasCalculator); super(opcode, name, stackItemsConsumed, stackItemsProduced, gasCalculator);
this.maxInitcodeSize = maxInitcodeSize;
} }
@Override @Override
@ -74,6 +78,10 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
final long inputOffset = clampedToLong(frame.getStackItem(1)); final long inputOffset = clampedToLong(frame.getStackItem(1));
final long inputSize = clampedToLong(frame.getStackItem(2)); final long inputSize = clampedToLong(frame.getStackItem(2));
if (inputSize > maxInitcodeSize) {
frame.popStackItems(getStackItemsConsumed());
return new OperationResult(cost, ExceptionalHaltReason.CODE_TOO_LARGE);
}
final Bytes inputData = frame.readMemory(inputOffset, inputSize); final Bytes inputData = frame.readMemory(inputOffset, inputSize);
Code code = evm.getCode(Hash.hash(inputData), inputData); Code code = evm.getCode(Hash.hash(inputData), inputData);

@ -29,8 +29,8 @@ public class Create2Operation extends AbstractCreateOperation {
private static final Bytes PREFIX = Bytes.fromHexString("0xFF"); private static final Bytes PREFIX = Bytes.fromHexString("0xFF");
public Create2Operation(final GasCalculator gasCalculator) { public Create2Operation(final GasCalculator gasCalculator, final int maxInitcodeSize) {
super(0xF5, "CREATE2", 4, 1, gasCalculator); super(0xF5, "CREATE2", 4, 1, gasCalculator, maxInitcodeSize);
} }
@Override @Override

@ -21,8 +21,8 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
public class CreateOperation extends AbstractCreateOperation { public class CreateOperation extends AbstractCreateOperation {
public CreateOperation(final GasCalculator gasCalculator) { public CreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) {
super(0xF0, "CREATE", 3, 1, gasCalculator); super(0xF0, "CREATE", 3, 1, gasCalculator, maxInitcodeSize);
} }
@Override @Override

@ -15,6 +15,8 @@
package org.hyperledger.besu.evm.operations; package org.hyperledger.besu.evm.operations;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID;
import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.CODE_TOO_LARGE;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -23,43 +25,65 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeFactory; import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.BlockValues; import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.Create2Operation; import org.hyperledger.besu.evm.operation.Create2Operation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult; import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.List;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
import org.junit.Before; import org.jetbrains.annotations.NotNull;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.runners.Parameterized; import org.junit.jupiter.params.provider.MethodSource;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class Create2OperationTest { public class Create2OperationTest {
private final String sender;
private final String salt;
private final String code;
private final String expectedAddress;
private final int expectedGas;
private MessageFrame messageFrame; private MessageFrame messageFrame;
private final WorldUpdater worldUpdater = mock(WorldUpdater.class); private final WorldUpdater worldUpdater = mock(WorldUpdater.class);
private final WrappedEvmAccount account = mock(WrappedEvmAccount.class); private final WrappedEvmAccount account = mock(WrappedEvmAccount.class);
private final MutableAccount mutableAccount = mock(MutableAccount.class); private final MutableAccount mutableAccount = mock(MutableAccount.class);
private final EVM evm = mock(EVM.class); private final EVM evm = mock(EVM.class);
private final WrappedEvmAccount newAccount = mock(WrappedEvmAccount.class);
private final MutableAccount newMutableAccount = mock(MutableAccount.class);
private final Create2Operation operation = private final Create2Operation operation =
new Create2Operation(new ConstantinopleGasCalculator()); new Create2Operation(new ConstantinopleGasCalculator(), Integer.MAX_VALUE);
private final Create2Operation maxInitCodeOperation =
new Create2Operation(
new ConstantinopleGasCalculator(), MainnetEVMs.SHANGHAI_INIT_CODE_SIZE_LIMIT);
private static final String TOPIC =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; // 32 FFs
private static final Bytes SIMPLE_CREATE =
Bytes.fromHexString(
"0x"
+ "7f" // push32
+ TOPIC
+ "6000" // PUSH1 0x00
+ "6000" // PUSH1 0x00
+ "A1" // LOG1
+ "6000" // PUSH1 0x00
+ "6000" // PUSH1 0x00
+ "F3" // RETURN
);
public static final String SENDER = "0xdeadc0de00000000000000000000000000000000";
private static final int SHANGHAI_CREATE_GAS = 41240 + (0xc000 / 32) * 6;
@Parameters(name = "sender: {0}, salt: {1}, code: {2}")
public static Object[][] params() { public static Object[][] params() {
return new Object[][] { return new Object[][] {
{ {
@ -114,21 +138,8 @@ public class Create2OperationTest {
}; };
} }
public Create2OperationTest( public void setUp(final String sender, final String salt, final String code) {
final String sender,
final String salt,
final String code,
final String expectedAddress,
final int expectedGas) {
this.sender = sender;
this.salt = salt;
this.code = code;
this.expectedAddress = expectedAddress;
this.expectedGas = expectedGas;
}
@Before
public void setUp() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final Bytes codeBytes = Bytes.fromHexString(code); final Bytes codeBytes = Bytes.fromHexString(code);
final UInt256 memoryLength = UInt256.valueOf(codeBytes.size()); final UInt256 memoryLength = UInt256.valueOf(codeBytes.size());
@ -171,16 +182,124 @@ public class Create2OperationTest {
invocation.getArgument(1), invocation.getArgument(0), 0, true)); invocation.getArgument(1), invocation.getArgument(0), 0, true));
} }
@Test @ParameterizedTest
public void shouldCalculateAddress() { @MethodSource("params")
void shouldCalculateAddress(
final String sender,
final String salt,
final String code,
final String expectedAddress,
final int ignoredExpectedGas) {
setUp(sender, salt, code);
final Address targetContractAddress = operation.targetContractAddress(messageFrame); final Address targetContractAddress = operation.targetContractAddress(messageFrame);
assertThat(targetContractAddress).isEqualTo(Address.fromHexString(expectedAddress)); assertThat(targetContractAddress).isEqualTo(Address.fromHexString(expectedAddress));
} }
@Test @ParameterizedTest
public void shouldCalculateGasPrice() { @MethodSource("params")
void shouldCalculateGasPrice(
final String sender,
final String salt,
final String code,
final String ignoredExpectedAddress,
final int expectedGas) {
setUp(sender, salt, code);
final OperationResult result = operation.execute(messageFrame, evm); final OperationResult result = operation.execute(messageFrame, evm);
assertThat(result.getHaltReason()).isNull(); assertThat(result.getHaltReason()).isNull();
assertThat(result.getGasCost()).isEqualTo(expectedGas); assertThat(result.getGasCost()).isEqualTo(expectedGas);
} }
@Test
void shanghaiMaxInitCodeSizeCreate() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.fromHexString("0xc000");
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final MessageFrame messageFrame =
testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1, messageFrameStack);
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.getOrCreate(any())).thenReturn(newAccount);
when(newAccount.getMutable()).thenReturn(newMutableAccount);
when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY);
when(worldUpdater.updater()).thenReturn(worldUpdater);
final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = maxInitCodeOperation.execute(messageFrame, evm);
final MessageFrame createFrame = messageFrameStack.peek();
final ContractCreationProcessor ccp =
new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of());
ccp.process(createFrame, OperationTracer.NO_TRACING);
final Log log = createFrame.getLogs().get(0);
final String calculatedTopic = log.getTopics().get(0).toUnprefixedHexString();
assertThat(calculatedTopic).isEqualTo(TOPIC);
assertThat(result.getGasCost()).isEqualTo(SHANGHAI_CREATE_GAS);
}
@Test
void shanghaiMaxInitCodeSizePlus1Create() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.fromHexString("0xc001");
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final MessageFrame messageFrame =
testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1, messageFrameStack);
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.getOrCreate(any())).thenReturn(newAccount);
when(newAccount.getMutable()).thenReturn(newMutableAccount);
when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY);
when(worldUpdater.updater()).thenReturn(worldUpdater);
final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = maxInitCodeOperation.execute(messageFrame, evm);
assertThat(result.getHaltReason()).isEqualTo(CODE_TOO_LARGE);
}
@NotNull
private MessageFrame testMemoryFrame(
final UInt256 memoryOffset,
final UInt256 memoryLength,
final UInt256 value,
final int depth,
final ArrayDeque<MessageFrame> messageFrameStack) {
final MessageFrame messageFrame =
MessageFrame.builder()
.type(MessageFrame.Type.CONTRACT_CREATION)
.contract(Address.ZERO)
.inputData(Bytes.EMPTY)
.sender(Address.fromHexString(SENDER))
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(CodeFactory.createCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE), 0, true))
.depth(depth)
.completer(__ -> {})
.address(Address.fromHexString(SENDER))
.blockHashLookup(n -> Hash.hash(Bytes.ofUnsignedLong(n)))
.blockValues(mock(BlockValues.class))
.gasPrice(Wei.ZERO)
.messageFrameStack(messageFrameStack)
.miningBeneficiary(Address.ZERO)
.originator(Address.ZERO)
.initialGas(100000L)
.worldUpdater(worldUpdater)
.build();
messageFrame.pushStackItem(Bytes.EMPTY);
messageFrame.pushStackItem(memoryLength);
messageFrame.pushStackItem(memoryOffset);
messageFrame.pushStackItem(value);
messageFrame.expandMemory(0, 500);
messageFrame.writeMemory(
memoryOffset.trimLeadingZeros().toInt(), SIMPLE_CREATE.size(), SIMPLE_CREATE);
return messageFrame;
}
} }

@ -17,6 +17,7 @@ package org.hyperledger.besu.evm.operations;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID; import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID;
import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.CODE_TOO_LARGE;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -54,7 +55,11 @@ public class CreateOperationTest {
private final WrappedEvmAccount newAccount = mock(WrappedEvmAccount.class); private final WrappedEvmAccount newAccount = mock(WrappedEvmAccount.class);
private final MutableAccount mutableAccount = mock(MutableAccount.class); private final MutableAccount mutableAccount = mock(MutableAccount.class);
private final MutableAccount newMutableAccount = mock(MutableAccount.class); private final MutableAccount newMutableAccount = mock(MutableAccount.class);
private final CreateOperation operation = new CreateOperation(new ConstantinopleGasCalculator()); private final CreateOperation operation =
new CreateOperation(new ConstantinopleGasCalculator(), Integer.MAX_VALUE);
private final CreateOperation maxInitCodeOperation =
new CreateOperation(
new ConstantinopleGasCalculator(), MainnetEVMs.SHANGHAI_INIT_CODE_SIZE_LIMIT);
private static final String TOPIC = private static final String TOPIC =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; // 32 FFs "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; // 32 FFs
@ -196,7 +201,7 @@ public class CreateOperationTest {
when(worldUpdater.updater()).thenReturn(worldUpdater); when(worldUpdater.updater()).thenReturn(worldUpdater);
final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm); var result = maxInitCodeOperation.execute(messageFrame, evm);
final MessageFrame createFrame = messageFrameStack.peek(); final MessageFrame createFrame = messageFrameStack.peek();
final ContractCreationProcessor ccp = final ContractCreationProcessor ccp =
new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of()); new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of());
@ -228,9 +233,8 @@ public class CreateOperationTest {
when(worldUpdater.updater()).thenReturn(worldUpdater); when(worldUpdater.updater()).thenReturn(worldUpdater);
final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm); var result = maxInitCodeOperation.execute(messageFrame, evm);
assertThat(messageFrame.getStackItem(0)).isEqualTo(UInt256.ZERO); assertThat(result.getHaltReason()).isEqualTo(CODE_TOO_LARGE);
assertThat(result.getGasCost()).isEqualTo(SHANGHAI_CREATE_GAS);
} }
@NotNull @NotNull

Loading…
Cancel
Save