Skip to content

Commit

Permalink
EIP-3860 Initcode Size Reference Test fixes (hyperledger#4911)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
shemnon authored and eum602 committed Nov 3, 2023
1 parent 69218cc commit 0145790
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.MainnetEVMs;
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.PrefixCodeRule;
import org.hyperledger.besu.evm.frame.MessageFrame;
Expand Down Expand Up @@ -532,7 +530,8 @@ static ProtocolSpecBuilder londonDefinition(
TransactionType.FRONTIER,
TransactionType.ACCESS_LIST,
TransactionType.EIP1559),
quorumCompatibilityMode))
quorumCompatibilityMode,
Integer.MAX_VALUE))
.transactionProcessorBuilder(
(gasCalculator,
transactionValidator,
Expand Down Expand Up @@ -655,9 +654,6 @@ static ProtocolSpecBuilder shanghaiDefinition(
? FeeMarket.zeroBaseFee(londonForkBlockNumber)
: 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(
chainId,
configContractSizeLimit,
Expand Down Expand Up @@ -690,17 +686,19 @@ static ProtocolSpecBuilder shanghaiDefinition(
londonFeeMarket,
CoinbaseFeePriceCalculator.eip1559()))
// Contract creation rules for EIP-3860 Limit and meter intitcode
.contractCreationProcessorBuilder(
(gasCalculator, evm) ->
new ContractCreationProcessor(
.transactionValidatorBuilder(
gasCalculator ->
new MainnetTransactionValidator(
gasCalculator,
evm,
londonFeeMarket,
true,
List.of(
MaxCodeSizeRule.of(contractSizeLimit),
CachedInvalidCodeRule.of(EvmSpecVersion.SHANGHAI)),
1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
chainId,
Set.of(
TransactionType.FRONTIER,
TransactionType.ACCESS_LIST,
TransactionType.EIP1559),
quorumCompatibilityMode,
SHANGHAI_INIT_CODE_SIZE_LIMIT))
.name("Shanghai");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public class MainnetTransactionValidator {
private final Set<TransactionType> acceptedTransactionTypes;
private final boolean goQuorumCompatibilityMode;

private final int maxInitcodeSize;

public MainnetTransactionValidator(
final GasCalculator gasCalculator,
final boolean checkSignatureMalleability,
Expand All @@ -78,7 +80,8 @@ public MainnetTransactionValidator(
checkSignatureMalleability,
chainId,
acceptedTransactionTypes,
quorumCompatibilityMode);
quorumCompatibilityMode,
Integer.MAX_VALUE);
}

public MainnetTransactionValidator(
Expand All @@ -87,13 +90,15 @@ public MainnetTransactionValidator(
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId,
final Set<TransactionType> acceptedTransactionTypes,
final boolean goQuorumCompatibilityMode) {
final boolean goQuorumCompatibilityMode,
final int maxInitcodeSize) {
this.gasCalculator = gasCalculator;
this.feeMarket = feeMarket;
this.disallowSignatureMalleability = checkSignatureMalleability;
this.chainId = chainId;
this.acceptedTransactionTypes = acceptedTransactionTypes;
this.goQuorumCompatibilityMode = goQuorumCompatibilityMode;
this.maxInitcodeSize = maxInitcodeSize;
}

/**
Expand Down Expand Up @@ -184,6 +189,14 @@ public ValidationResult<TransactionInvalidReason> validate(
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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public enum TransactionInvalidReason {
TRANSACTION_ALREADY_KNOWN,
TRANSACTION_REPLACEMENT_UNDERPRICED,
MAX_PRIORITY_FEE_PER_GAS_EXCEEDS_MAX_FEE_PER_GAS,
INITCODE_TOO_LARGE,
// Private Transaction Invalid Reasons
PRIVATE_TRANSACTION_FAILED,
PRIVATE_NONCE_TOO_LOW,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
import java.math.BigInteger;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
Expand Down Expand Up @@ -259,7 +259,8 @@ public void shouldRejectTransactionWithMaxPriorityFeeGreaterThanMaxFee() {
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.values()),
defaultGoQuorumCompatibilityMode);
defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
validator.setTransactionFilter(transactionFilter(true));

final Transaction transaction =
Expand Down Expand Up @@ -342,7 +343,8 @@ public void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() {
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER),
defaultGoQuorumCompatibilityMode);
defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);

final MainnetTransactionValidator eip1559Validator =
new MainnetTransactionValidator(
Expand All @@ -351,7 +353,8 @@ public void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() {
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode);
defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);

final Transaction transaction =
new TransactionTestFixture()
Expand Down Expand Up @@ -383,7 +386,8 @@ public void shouldRejectTransactionIfEIP1559TransactionGasPriceLessBaseFee() {
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode);
defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction =
new TransactionTestFixture()
.type(TransactionType.EIP1559)
Expand All @@ -406,7 +410,8 @@ public void shouldAcceptZeroGasPriceTransactionIfBaseFeeIsZero() {
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode);
defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction =
new TransactionTestFixture()
.type(TransactionType.EIP1559)
Expand All @@ -428,7 +433,8 @@ public void shouldAcceptValidEIP1559() {
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode);
defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction =
new TransactionTestFixture()
.maxPriorityFeePerGas(Optional.of(Wei.of(1)))
Expand All @@ -452,7 +458,8 @@ public void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransaction
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559),
defaultGoQuorumCompatibilityMode);
defaultGoQuorumCompatibilityMode,
Integer.MAX_VALUE);
final Transaction transaction =
new TransactionTestFixture()
.maxPriorityFeePerGas(Optional.of(Wei.of(1)))
Expand All @@ -468,6 +475,33 @@ public void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransaction
.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
public void goQuorumCompatibilityModeRejectNonZeroGasPrice() {
final MainnetTransactionValidator validator =
Expand Down
9 changes: 7 additions & 2 deletions evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ public class MainnetEVMs {

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() {
// utility class
}
Expand Down Expand Up @@ -209,7 +212,7 @@ public static void registerFrontierOperations(
registry.put(new InvalidOperation(gasCalculator));
registry.put(new StopOperation(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 CallCodeOperation(gasCalculator));

Expand Down Expand Up @@ -317,7 +320,7 @@ public static OperationRegistry constantinopleOperations(final GasCalculator gas
public static void registerConstantinopleOperations(
final OperationRegistry registry, final GasCalculator 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 ShlOperation(gasCalculator));
registry.put(new ShrOperation(gasCalculator));
Expand Down Expand Up @@ -461,6 +464,8 @@ public static void registerShanghaiOperations(
final BigInteger chainID) {
registerParisOperations(registry, gasCalculator, chainID);
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
protected static final OperationResult UNDERFLOW_RESPONSE =
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);

protected int maxInitcodeSize;

protected AbstractCreateOperation(
final int opcode,
final String name,
final int stackItemsConsumed,
final int stackItemsProduced,
final GasCalculator gasCalculator) {
final GasCalculator gasCalculator,
final int maxInitcodeSize) {
super(opcode, name, stackItemsConsumed, stackItemsProduced, gasCalculator);
this.maxInitcodeSize = maxInitcodeSize;
}

@Override
Expand Down Expand Up @@ -74,6 +78,10 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) {

final long inputOffset = clampedToLong(frame.getStackItem(1));
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);
Code code = evm.getCode(Hash.hash(inputData), inputData);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public class Create2Operation extends AbstractCreateOperation {

private static final Bytes PREFIX = Bytes.fromHexString("0xFF");

public Create2Operation(final GasCalculator gasCalculator) {
super(0xF5, "CREATE2", 4, 1, gasCalculator);
public Create2Operation(final GasCalculator gasCalculator, final int maxInitcodeSize) {
super(0xF5, "CREATE2", 4, 1, gasCalculator, maxInitcodeSize);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@

public class CreateOperation extends AbstractCreateOperation {

public CreateOperation(final GasCalculator gasCalculator) {
super(0xF0, "CREATE", 3, 1, gasCalculator);
public CreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) {
super(0xF0, "CREATE", 3, 1, gasCalculator, maxInitcodeSize);
}

@Override
Expand Down
Loading

0 comments on commit 0145790

Please sign in to comment.