Skip to content

Commit

Permalink
EIP-7692 "Mega" EOF Implementation (#7169)
Browse files Browse the repository at this point in the history
A complete and up to date implementation of EIP-7692 EOF v.1. For genesis 
file activation use "PragueEOFTime", for references tests it activates as 
part of Prague.

Signed-off-by: Danno Ferrin <[email protected]>
  • Loading branch information
shemnon committed Jun 12, 2024
1 parent 365737c commit 85d286a
Show file tree
Hide file tree
Showing 123 changed files with 6,178 additions and 1,899 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- Improve the selection of the most profitable built block [#7174](https://github.com/hyperledger/besu/pull/7174)
- Support for eth_maxPriorityFeePerGas [#5658](https://github.com/hyperledger/besu/issues/5658)
- Enable continuous profiling with default setting [#7006](https://github.com/hyperledger/besu/pull/7006)
- A full and up to date implementation of EOF for Prague [#7169](https://github.com/hyperledger/besu/pull/7169)

### Bug fixes
- Make `eth_gasPrice` aware of the base fee market [#7102](https://github.com/hyperledger/besu/pull/7102)
Expand Down
3 changes: 2 additions & 1 deletion besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -1504,7 +1504,8 @@ private void configureNativeLibs() {
}

if (genesisConfigOptionsSupplier.get().getCancunTime().isPresent()
|| genesisConfigOptionsSupplier.get().getPragueTime().isPresent()) {
|| genesisConfigOptionsSupplier.get().getPragueTime().isPresent()
|| genesisConfigOptionsSupplier.get().getPragueEOFTime().isPresent()) {
if (kzgTrustedSetupFile != null) {
KZGPointEvalPrecompiledContract.init(kzgTrustedSetupFile);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,13 @@ default boolean isConsensusMigration() {
*/
OptionalLong getPragueTime();

/**
* Gets Prague EOF time.
*
* @return the prague time
*/
OptionalLong getPragueEOFTime();

/**
* Gets future eips time.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ public OptionalLong getPragueTime() {
return getOptionalLong("praguetime");
}

@Override
public OptionalLong getPragueEOFTime() {
return getOptionalLong("pragueeoftime");
}

@Override
public OptionalLong getFutureEipsTime() {
return getOptionalLong("futureeipstime");
Expand Down Expand Up @@ -457,6 +462,7 @@ public Map<String, Object> asMap() {
getShanghaiTime().ifPresent(l -> builder.put("shanghaiTime", l));
getCancunTime().ifPresent(l -> builder.put("cancunTime", l));
getPragueTime().ifPresent(l -> builder.put("pragueTime", l));
getPragueEOFTime().ifPresent(l -> builder.put("pragueEOFTime", l));
getTerminalBlockNumber().ifPresent(l -> builder.put("terminalBlockNumber", l));
getTerminalBlockHash().ifPresent(h -> builder.put("terminalBlockHash", h.toHexString()));
getFutureEipsTime().ifPresent(l -> builder.put("futureEipsTime", l));
Expand Down Expand Up @@ -605,6 +611,7 @@ public List<Long> getForkBlockTimestamps() {
getShanghaiTime(),
getCancunTime(),
getPragueTime(),
getPragueEOFTime(),
getFutureEipsTime(),
getExperimentalEipsTime());
// when adding forks add an entry to ${REPO_ROOT}/config/src/test/resources/all_forks.json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
private OptionalLong shanghaiTime = OptionalLong.empty();
private OptionalLong cancunTime = OptionalLong.empty();
private OptionalLong pragueTime = OptionalLong.empty();
private OptionalLong pragueEOFTime = OptionalLong.empty();
private OptionalLong futureEipsTime = OptionalLong.empty();
private OptionalLong experimentalEipsTime = OptionalLong.empty();
private OptionalLong terminalBlockNumber = OptionalLong.empty();
Expand Down Expand Up @@ -242,6 +243,11 @@ public OptionalLong getPragueTime() {
return pragueTime;
}

@Override
public OptionalLong getPragueEOFTime() {
return pragueEOFTime;
}

@Override
public OptionalLong getFutureEipsTime() {
return futureEipsTime;
Expand Down Expand Up @@ -635,6 +641,18 @@ public StubGenesisConfigOptions pragueTime(final long timestamp) {
return this;
}

/**
* PragueEOF time.
*
* @param timestamp the timestamp
* @return the stub genesis config options
*/
public StubGenesisConfigOptions pragueEOFTime(final long timestamp) {
pragueTime = OptionalLong.of(timestamp);
pragueEOFTime = pragueTime;
return this;
}

/**
* Future EIPs Time block.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ void shouldGetPragueTime() {
assertThat(config.getPragueTime()).hasValue(1670470143);
}

@Test
void shouldGetPragueEOFTime() {
final GenesisConfigOptions config =
fromConfigOptions(singletonMap("pragueEOFTime", 1670470143));
assertThat(config.getPragueEOFTime()).hasValue(1670470143);
}

@Test
void shouldGetFutureEipsTime() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("futureEipsTime", 1337));
Expand Down Expand Up @@ -232,6 +239,7 @@ void shouldNotReturnEmptyOptionalWhenBlockNumberNotSpecified() {
assertThat(config.getShanghaiTime()).isEmpty();
assertThat(config.getCancunTime()).isEmpty();
assertThat(config.getPragueTime()).isEmpty();
assertThat(config.getPragueEOFTime()).isEmpty();
assertThat(config.getFutureEipsTime()).isEmpty();
assertThat(config.getExperimentalEipsTime()).isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,14 @@ private static boolean isPragueAtGenesis(final GenesisConfigFile genesis) {
if (pragueTimestamp.isPresent()) {
return genesis.getTimestamp() >= pragueTimestamp.getAsLong();
}
return isPragueEOFAtGenesis(genesis);
}

private static boolean isPragueEOFAtGenesis(final GenesisConfigFile genesis) {
final OptionalLong pragueEOFTimestamp = genesis.getConfigOptions().getPragueEOFTime();
if (pragueEOFTimestamp.isPresent()) {
return genesis.getTimestamp() >= pragueEOFTimestamp.getAsLong();
}
return isFutureEipsTimeAtGenesis(genesis);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ public ProtocolSpecBuilder pragueDefinition(final GenesisConfigOptions genesisCo
miningParameters);
}

public ProtocolSpecBuilder pragueEOFDefinition(final GenesisConfigOptions genesisConfigOptions) {
return MainnetProtocolSpecs.pragueEOFDefinition(
chainId,
contractSizeLimit,
evmStackSize,
isRevertReasonEnabled,
genesisConfigOptions,
evmConfiguration,
miningParameters);
}

/**
* The "future" fork consists of EIPs that have been approved for Ethereum Mainnet but not
* scheduled for a fork. This is also known as "Eligible For Inclusion" (EFI) or "Considered for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
Expand Down Expand Up @@ -735,9 +736,6 @@ static ProtocolSpecBuilder pragueDefinition(
final GenesisConfigOptions genesisConfigOptions,
final EvmConfiguration evmConfiguration,
final MiningParameters miningParameters) {
final int contractSizeLimit =
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE);

final Address depositContractAddress =
genesisConfigOptions.getDepositContractAddress().orElse(DEFAULT_DEPOSIT_CONTRACT_ADDRESS);
Expand All @@ -750,47 +748,64 @@ static ProtocolSpecBuilder pragueDefinition(
genesisConfigOptions,
evmConfiguration,
miningParameters)
// EVM changes to support EOF EIPs (3670, 4200, 4750, 5450)
// EIP-3074 AUTH and AUTCALL gas
.gasCalculator(PragueGasCalculator::new)
// EIP-3074 AUTH and AUTCALL
.evmBuilder(
(gasCalculator, jdCacheConfig) ->
MainnetEVMs.prague(
gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration))
// change contract call creator to accept EOF code

// EIP-2537 BLS12-381 precompiles
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague)

// EIP-7002 Withdrawls / EIP-6610 Deposits / EIP-7685 Requests
.requestsValidator(pragueRequestsValidator(depositContractAddress))
// EIP-7002 Withdrawls / EIP-6610 Deposits / EIP-7685 Requests
.requestProcessorCoordinator(pragueRequestsProcessors(depositContractAddress))

// EIP-2935 Blockhash processor
.blockHashProcessor(new PragueBlockHashProcessor())
.name("Prague");
}

static ProtocolSpecBuilder pragueEOFDefinition(
final Optional<BigInteger> chainId,
final OptionalInt configContractSizeLimit,
final OptionalInt configStackSizeLimit,
final boolean enableRevertReason,
final GenesisConfigOptions genesisConfigOptions,
final EvmConfiguration evmConfiguration,
final MiningParameters miningParameters) {
final int contractSizeLimit =
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);

return pragueDefinition(
chainId,
configContractSizeLimit,
configStackSizeLimit,
enableRevertReason,
genesisConfigOptions,
evmConfiguration,
miningParameters)
// EIP-7692 EOF v1 Gas calculator
.gasCalculator(PragueEOFGasCalculator::new)
// EIP-7692 EOF v1 EVM and opcodes
.evmBuilder(
(gasCalculator, jdCacheConfig) ->
MainnetEVMs.pragueEOF(
gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration))
// EIP-7698 EOF v1 creation transaction
.contractCreationProcessorBuilder(
(gasCalculator, evm) ->
new ContractCreationProcessor(
gasCalculator,
evm,
true,
List.of(
MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)),
List.of(MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1)),
1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
// warm blockahsh contract
.transactionProcessorBuilder(
(gasCalculator,
feeMarket,
transactionValidator,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidator,
contractCreationProcessor,
messageCallProcessor,
true,
true,
stackSizeLimit,
feeMarket,
CoinbaseFeePriceCalculator.eip1559()))

// use prague precompiled contracts
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague)
.requestsValidator(pragueRequestsValidator(depositContractAddress))
.requestProcessorCoordinator(pragueRequestsProcessors(depositContractAddress))
.blockHashProcessor(new PragueBlockHashProcessor())
.name("Prague");
.name("PragueEOF");
}

static ProtocolSpecBuilder futureEipsDefinition(
Expand All @@ -803,7 +818,7 @@ static ProtocolSpecBuilder futureEipsDefinition(
final MiningParameters miningParameters) {
final int contractSizeLimit =
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
return pragueDefinition(
return pragueEOFDefinition(
chainId,
configContractSizeLimit,
configStackSizeLimit,
Expand All @@ -823,8 +838,7 @@ static ProtocolSpecBuilder futureEipsDefinition(
gasCalculator,
evm,
true,
List.of(
MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)),
List.of(MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1)),
1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
// use future configured precompiled contracts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeInvalid;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
Expand Down Expand Up @@ -382,13 +384,14 @@ public TransactionProcessingResult processTransaction(
Address.contractAddress(senderAddress, sender.getNonce() - 1L);

final Bytes initCodeBytes = transaction.getPayload();
Code code = contractCreationProcessor.getCodeFromEVMForCreation(initCodeBytes);
initialFrame =
commonMessageFrameBuilder
.type(MessageFrame.Type.CONTRACT_CREATION)
.address(contractAddress)
.contract(contractAddress)
.inputData(Bytes.EMPTY)
.code(contractCreationProcessor.getCodeFromEVMUncached(initCodeBytes))
.inputData(initCodeBytes.slice(code.getSize()))
.code(code)
.build();
} else {
@SuppressWarnings("OptionalGetWithoutIsPresent") // isContractCall tests isPresent
Expand All @@ -415,12 +418,17 @@ public TransactionProcessingResult processTransaction(
} else {
initialFrame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
initialFrame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INVALID_CODE));
validationResult =
ValidationResult.invalid(
TransactionInvalidReason.EOF_CODE_INVALID,
((CodeInvalid) initialFrame.getCode()).getInvalidReason());
}

if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
worldUpdater.commit();
} else {
if (initialFrame.getExceptionalHaltReason().isPresent()) {
if (initialFrame.getExceptionalHaltReason().isPresent()
&& initialFrame.getCode().isValid()) {
validationResult =
ValidationResult.invalid(
TransactionInvalidReason.EXECUTION_HALTED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ private void validateEthereumForkOrdering() {
lastForkBlock = validateForkOrder("Shanghai", config.getShanghaiTime(), lastForkBlock);
lastForkBlock = validateForkOrder("Cancun", config.getCancunTime(), lastForkBlock);
lastForkBlock = validateForkOrder("Prague", config.getPragueTime(), lastForkBlock);
lastForkBlock = validateForkOrder("PragueEOF", config.getPragueEOFTime(), lastForkBlock);
lastForkBlock = validateForkOrder("FutureEips", config.getFutureEipsTime(), lastForkBlock);
lastForkBlock =
validateForkOrder("ExperimentalEips", config.getExperimentalEipsTime(), lastForkBlock);
Expand Down Expand Up @@ -331,6 +332,7 @@ private Stream<Optional<BuilderMapEntry>> createMilestones(
timestampMilestone(config.getShanghaiTime(), specFactory.shanghaiDefinition(config)),
timestampMilestone(config.getCancunTime(), specFactory.cancunDefinition(config)),
timestampMilestone(config.getPragueTime(), specFactory.pragueDefinition(config)),
timestampMilestone(config.getPragueEOFTime(), specFactory.pragueEOFDefinition(config)),
timestampMilestone(config.getFutureEipsTime(), specFactory.futureEipsDefinition(config)),
timestampMilestone(
config.getExperimentalEipsTime(), specFactory.experimentalEipsDefinition(config)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeV0;
Expand Down Expand Up @@ -138,13 +139,14 @@ public TransactionProcessingResult processTransaction(
privacyGroupId);

final Bytes initCodeBytes = transaction.getPayload();
Code code = contractCreationProcessor.getCodeFromEVMForCreation(initCodeBytes);
initialFrame =
commonMessageFrameBuilder
.type(MessageFrame.Type.CONTRACT_CREATION)
.address(privateContractAddress)
.contract(privateContractAddress)
.inputData(Bytes.EMPTY)
.code(contractCreationProcessor.getCodeFromEVMUncached(initCodeBytes))
.inputData(initCodeBytes.slice(code.getSize()))
.code(code)
.build();
} else {
final Address to = transaction.getTo().get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public enum TransactionInvalidReason {
INVALID_BLOBS,
PLUGIN_TX_POOL_VALIDATOR,
EXECUTION_HALTED,
EOF_CODE_INVALID,
// Private Transaction Invalid Reasons
PRIVATE_TRANSACTION_INVALID,
PRIVATE_TRANSACTION_FAILED,
Expand Down
Loading

0 comments on commit 85d286a

Please sign in to comment.