Add parameters to EVM library fluent API (#5930)

Add the ability to configure more parameters in the fluent API.
Specifically contract address, coinbase, difficulty, mixHash/prevRandao,
baseFee, block number, timestamp, gas limit, previous block hashes, and
versioned hashes. Also create EVM forks parametrically instead of by 
a method name.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
pull/5980/head
Danno Ferrin 1 year ago committed by GitHub
parent 10b956f75f
commit 13a934d6b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java
  2. 13
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  3. 57
      ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-cold.json
  4. 57
      ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-warm.json
  5. 25
      evm/src/main/java/org/hyperledger/besu/evm/EVM.java
  6. 24
      evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java
  7. 39
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  8. 538
      evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java
  9. 17
      evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java
  10. 100
      evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleBlockValues.java
  11. 34
      evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java
  12. 17
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java
  13. 15
      evm/src/main/java/org/hyperledger/besu/evm/operation/ChainIdOperation.java
  14. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java
  15. 11
      evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java
  16. 5
      evm/src/test/java/org/hyperledger/besu/evm/StandardJsonTracerTest.java
  17. 229
      evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java
  18. 2
      evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java
  19. 4
      evm/src/test/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessorTest.java

@ -113,7 +113,13 @@ public class Address extends DelegatingBytes {
"An account address must be %s bytes long, got %s",
SIZE,
value.size());
return new Address(value);
if (value instanceof Address address) {
return address;
} else if (value instanceof DelegatingBytes delegatingBytes) {
return new Address(delegatingBytes.copy());
} else {
return new Address(value);
}
}
/**

@ -59,6 +59,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.NavigableMap;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.base.Joiner;
@ -134,6 +135,12 @@ public class EvmToolCommand implements Runnable {
description = "Receiving address for this invocation.")
private final Address receiver = Address.fromHexString("0x00");
@Option(
names = {"--coinbase"},
paramLabel = "<address>",
description = "Coinbase for this invocation.")
private final Address coinbase = Address.fromHexString("0x00");
@Option(
names = {"--input"},
paramLabel = "<code>",
@ -317,7 +324,7 @@ public class EvmToolCommand implements Runnable {
final BlockHeader blockHeader =
BlockHeaderBuilder.create()
.parentHash(Hash.EMPTY)
.coinbase(Address.ZERO)
.coinbase(coinbase)
.difficulty(Difficulty.ONE)
.number(1)
.gasLimit(5000)
@ -399,6 +406,10 @@ public class EvmToolCommand implements Runnable {
.completer(c -> {})
.miningBeneficiary(blockHeader.getCoinbase())
.blockHashLookup(new CachingBlockHashLookup(blockHeader, component.getBlockchain()))
.accessListWarmAddresses(
EvmSpecVersion.SHANGHAI.compareTo(evm.getEvmVersion()) <= 0
? Set.of(coinbase)
: Set.of())
.build();
Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack();

@ -0,0 +1,57 @@
{
"cli": [
"--notime",
"--json",
"--code",
"4131ff",
"--coinbase",
"4444588443C3A91288C5002483449ABA1054192B",
"--fork",
"paris"
],
"stdin": "",
"stdout": [
{
"pc": 0,
"op": 65,
"gas": "0x2540b91f8",
"gasCost": "0x2",
"memSize": 0,
"stack": [],
"depth": 1,
"refund": 0,
"opName": "COINBASE"
},
{
"pc": 1,
"op": 49,
"gas": "0x2540b91f6",
"gasCost": "0xa28",
"memSize": 0,
"stack": [
"0x4444588443c3a91288c5002483449aba1054192b"
],
"depth": 1,
"refund": 0,
"opName": "BALANCE"
},
{
"pc": 2,
"op": 255,
"gas": "0x2540b87ce",
"gasCost": "0x1388",
"memSize": 0,
"stack": [
"0x0"
],
"depth": 1,
"refund": 0,
"opName": "SELFDESTRUCT"
},
{
"gasUser": "0x1db2",
"gasTotal": "0x1db2",
"output": "0x"
}
]
}

@ -0,0 +1,57 @@
{
"cli": [
"--notime",
"--json",
"--code",
"4131ff",
"--coinbase",
"4444588443C3A91288C5002483449ABA1054192B",
"--fork",
"shanghai"
],
"stdin": "",
"stdout": [
{
"pc": 0,
"op": 65,
"gas": "0x2540b91f8",
"gasCost": "0x2",
"memSize": 0,
"stack": [],
"depth": 1,
"refund": 0,
"opName": "COINBASE"
},
{
"pc": 1,
"op": 49,
"gas": "0x2540b91f6",
"gasCost": "0x64",
"memSize": 0,
"stack": [
"0x4444588443c3a91288c5002483449aba1054192b"
],
"depth": 1,
"refund": 0,
"opName": "BALANCE"
},
{
"pc": 2,
"op": 255,
"gas": "0x2540b9192",
"gasCost": "0x1388",
"memSize": 0,
"stack": [
"0x0"
],
"depth": 1,
"refund": 0,
"opName": "SELFDESTRUCT"
},
{
"gasUser": "0x13ee",
"gasTotal": "0x13ee",
"output": "0x"
}
]
}

@ -31,6 +31,7 @@ import org.hyperledger.besu.evm.operation.AddModOperation;
import org.hyperledger.besu.evm.operation.AddOperation;
import org.hyperledger.besu.evm.operation.AndOperation;
import org.hyperledger.besu.evm.operation.ByteOperation;
import org.hyperledger.besu.evm.operation.ChainIdOperation;
import org.hyperledger.besu.evm.operation.DivOperation;
import org.hyperledger.besu.evm.operation.DupOperation;
import org.hyperledger.besu.evm.operation.ExpOperation;
@ -130,6 +131,30 @@ public class EVM {
return evmSpecVersion.maxEofVersion;
}
/**
* Returns the configured EVM spec version for this EVM
*
* @return the evm spec version
*/
public EvmSpecVersion getEvmVersion() {
return evmSpecVersion;
}
/**
* Return the ChainId this Executor is using, or empty if the EVM version does not expose chain
* ID.
*
* @return the ChainId, or empty if not exposed.
*/
public Optional<Bytes> getChainId() {
Operation op = operations.get(ChainIdOperation.OPCODE);
if (op instanceof ChainIdOperation chainIdOperation) {
return Optional.of(chainIdOperation.getChainId());
} else {
return Optional.empty();
}
}
/**
* Run to halt.
*

@ -15,6 +15,9 @@
*/
package org.hyperledger.besu.evm;
import java.util.Comparator;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -24,6 +27,10 @@ public enum EvmSpecVersion {
FRONTIER(0, true, "Frontier", "Finalized"),
/** Homestead evm spec version. */
HOMESTEAD(0, true, "Homestead", "Finalized"),
/** Tangerine Whistle evm spec version. */
TANGERINE_WHISTLE(0, true, "Tangerine Whistle", "Finalized"),
/** Spurious Dragon evm spec version. */
SPURIOUS_DRAGON(0, true, "Spuruous Dragon", "Finalized"),
/** Byzantium evm spec version. */
BYZANTIUM(0, true, "Byzantium", "Finalized"),
/** Constantinople evm spec version. */
@ -47,7 +54,7 @@ public enum EvmSpecVersion {
/** Osaka evm spec version. */
OSAKA(0, false, "Osaka", "Placeholder"),
/** Bogota evm spec version. */
BOGOTA(0, false, "Bogata", "Placeholder"),
BOGOTA(0, false, "Bogota", "Placeholder"),
/** Development fork for unscheduled EIPs */
FUTURE_EIPS(1, false, "Future_EIPs", "Development, for accepted and unscheduled EIPs"),
/** Development fork for EIPs not accepted to Mainnet */
@ -134,7 +141,7 @@ public enum EvmSpecVersion {
/**
* Calculate a spec version from a text fork name.
*
* @param name The name of the fork, such as "shahghai" or "berlin"
* @param name The name of the fork, such as "shanghai" or "berlin"
* @return the EVM spec version for that fork, or null if no fork matched.
*/
public static EvmSpecVersion fromName(final String name) {
@ -145,4 +152,17 @@ public enum EvmSpecVersion {
}
return null;
}
/**
* The most recent deployed evm supported by the library. This will change across versions and
* will be updated after mainnet activations.
*
* @return the most recently activated mainnet spec.
*/
public static EvmSpecVersion mostRecent() {
return Stream.of(EvmSpecVersion.values())
.filter(v -> v.specFinalized)
.max(Comparator.naturalOrder())
.orElseThrow();
}
}

@ -20,6 +20,7 @@ import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.HomesteadGasCalculator;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator;
@ -278,7 +279,7 @@ public class MainnetEVMs {
* @return the evm
*/
public static EVM homestead(final EvmConfiguration evmConfiguration) {
return homestead(new FrontierGasCalculator(), evmConfiguration);
return homestead(new HomesteadGasCalculator(), evmConfiguration);
}
/**
@ -328,7 +329,12 @@ public class MainnetEVMs {
* @return the evm
*/
public static EVM spuriousDragon(final EvmConfiguration evmConfiguration) {
return homestead(new SpuriousDragonGasCalculator(), evmConfiguration);
GasCalculator gasCalculator = new SpuriousDragonGasCalculator();
return new EVM(
homesteadOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.SPURIOUS_DRAGON);
}
/**
@ -338,7 +344,12 @@ public class MainnetEVMs {
* @return the evm
*/
public static EVM tangerineWhistle(final EvmConfiguration evmConfiguration) {
return homestead(new TangerineWhistleGasCalculator(), evmConfiguration);
GasCalculator gasCalculator = new TangerineWhistleGasCalculator();
return new EVM(
homesteadOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.TANGERINE_WHISTLE);
}
/**
@ -413,11 +424,16 @@ public class MainnetEVMs {
*/
public static EVM constantinople(
final GasCalculator gasCalculator, final EvmConfiguration evmConfiguration) {
var version = EvmSpecVersion.CONSTANTINOPLE;
return constantiNOPEl(gasCalculator, evmConfiguration, version);
}
private static EVM constantiNOPEl(
final GasCalculator gasCalculator,
final EvmConfiguration evmConfiguration,
final EvmSpecVersion version) {
return new EVM(
constantinopleOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.CONSTANTINOPLE);
constantinopleOperations(gasCalculator), gasCalculator, evmConfiguration, version);
}
/**
@ -455,7 +471,8 @@ public class MainnetEVMs {
* @return the evm
*/
public static EVM petersburg(final EvmConfiguration evmConfiguration) {
return constantinople(new PetersburgGasCalculator(), evmConfiguration);
return constantiNOPEl(
new PetersburgGasCalculator(), evmConfiguration, EvmSpecVersion.PETERSBURG);
}
/**
@ -1145,7 +1162,7 @@ public class MainnetEVMs {
* @return the evm
*/
public static EVM experimentalEips(final EvmConfiguration evmConfiguration) {
return futureEips(DEV_NET_CHAIN_ID, evmConfiguration);
return experimentalEips(DEV_NET_CHAIN_ID, evmConfiguration);
}
/**
@ -1157,7 +1174,7 @@ public class MainnetEVMs {
*/
public static EVM experimentalEips(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
return futureEips(chainId, evmConfiguration);
return experimentalEips(new CancunGasCalculator(), chainId, evmConfiguration);
}
/**
@ -1176,7 +1193,7 @@ public class MainnetEVMs {
experimentalEipsOperations(gasCalculator, chainId),
gasCalculator,
evmConfiguration,
EvmSpecVersion.FUTURE_EIPS);
EvmSpecVersion.EXPERIMENTAL_EIPS);
}
/**

@ -18,9 +18,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.contractvalidation.ContractValidationRule;
@ -36,14 +38,19 @@ import org.hyperledger.besu.evm.processor.MessageCallProcessor;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.errorprone.annotations.InlineMe;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
@ -57,22 +64,27 @@ public class EVMExecutor {
private long gas = Long.MAX_VALUE;
private Address receiver = Address.ZERO;
private Address sender = Address.ZERO;
private Address contract = Address.ZERO;
private Address coinbase = Address.ZERO;
private Wei gasPriceGWei = Wei.ZERO;
private Wei blobGasPrice = Wei.ZERO;
private Bytes callData = Bytes.EMPTY;
private Wei ethValue = Wei.ZERO;
private Code code = CodeV0.EMPTY_CODE;
private BlockValues blockValues = new SimpleBlockValues();
private Function<Long, Hash> blockHashLookup = h -> null;
private Optional<List<VersionedHash>> versionedHashes = Optional.empty();
private OperationTracer tracer = OperationTracer.NO_TRACING;
private boolean requireDeposit = true;
private List<ContractValidationRule> contractValidationRules =
List.of(MaxCodeSizeRule.of(0x6000), PrefixCodeRule.of());
private long initialNonce = 0;
private long initialNonce = 1;
private Collection<Address> forceCommitAddresses = List.of(Address.fromHexString("0x03"));
private Set<Address> accessListWarmAddresses = Set.of();
private Set<Address> accessListWarmAddresses = new HashSet<>();
private Multimap<Address, Bytes32> accessListWarmStorage = HashMultimap.create();
private MessageCallProcessor messageCallProcessor = null;
private ContractCreationProcessor contractCreationProcessor = null;
private MessageFrame.Type messageFrameType = MessageFrame.Type.MESSAGE_CALL;
private EVMExecutor(final EVM evm) {
checkNotNull(evm, "evm must not be null");
@ -80,7 +92,84 @@ public class EVMExecutor {
}
/**
* Instandiate Evm executor.
* Create an EVM with the most current activated fork on chain ID 1.
*
* <p>Note, this will change across versions
*
* @return executor builder
*/
public static EVMExecutor evm() {
return evm(EvmSpecVersion.mostRecent());
}
/**
* Create an EVM at the specified version with chain ID 1.
*
* @param fork the EVM spec version to use
* @return executor builder
*/
public static EVMExecutor evm(final EvmSpecVersion fork) {
return evm(fork, BigInteger.ONE);
}
/**
* Create an EVM at the specified version and chain ID
*
* @param fork the EVM spec version to use
* @param chainId the chain ID to use
* @return executor builder
*/
public static EVMExecutor evm(final EvmSpecVersion fork, final BigInteger chainId) {
return evm(fork, chainId, EvmConfiguration.DEFAULT);
}
/**
* Create an EVM at the specified version and chain ID
*
* @param fork the EVM spec version to use
* @param chainId the chain ID to use
* @return executor builder
*/
public static EVMExecutor evm(final EvmSpecVersion fork, final Bytes chainId) {
return evm(fork, new BigInteger(1, chainId.toArrayUnsafe()), EvmConfiguration.DEFAULT);
}
/**
* Create an EVM at the specified version and chain ID
*
* @param fork the EVM spec version to use
* @param chainId the chain ID to use
* @param evmConfiguration system configuration options.
* @return executor builder
*/
public static EVMExecutor evm(
final EvmSpecVersion fork,
final BigInteger chainId,
final EvmConfiguration evmConfiguration) {
return switch (fork) {
case FRONTIER -> frontier(evmConfiguration);
case HOMESTEAD -> homestead(evmConfiguration);
case TANGERINE_WHISTLE -> tangerineWhistle(evmConfiguration);
case SPURIOUS_DRAGON -> spuriousDragon(evmConfiguration);
case BYZANTIUM -> byzantium(evmConfiguration);
case CONSTANTINOPLE -> constantinople(evmConfiguration);
case PETERSBURG -> petersburg(evmConfiguration);
case ISTANBUL -> istanbul(chainId, evmConfiguration);
case BERLIN -> berlin(chainId, evmConfiguration);
case LONDON -> london(chainId, evmConfiguration);
case PARIS -> paris(chainId, evmConfiguration);
case SHANGHAI -> shanghai(chainId, evmConfiguration);
case CANCUN -> cancun(chainId, evmConfiguration);
case PRAGUE -> prague(chainId, evmConfiguration);
case OSAKA -> osaka(chainId, evmConfiguration);
case BOGOTA -> bogota(chainId, evmConfiguration);
case FUTURE_EIPS -> futureEips(chainId, evmConfiguration);
case EXPERIMENTAL_EIPS -> experimentalEips(chainId, evmConfiguration);
};
}
/**
* Instantiate Evm executor.
*
* @param evm the evm
* @return the evm executor
@ -102,6 +191,7 @@ public class EVMExecutor {
executor.contractValidationRules = List.of();
executor.requireDeposit = false;
executor.forceCommitAddresses = List.of();
executor.initialNonce = 0;
return executor;
}
@ -117,31 +207,33 @@ public class EVMExecutor {
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of();
executor.forceCommitAddresses = List.of();
executor.initialNonce = 0;
return executor;
}
/**
* Instantiate Spurious dragon evm executor.
* Instantiate Tangerine whistle evm executor.
*
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor spuriousDragon(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.spuriousDragon(evmConfiguration));
public static EVMExecutor tangerineWhistle(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.tangerineWhistle(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
executor.initialNonce = 0;
return executor;
}
/**
* Instantiate Tangerine whistle evm executor.
* Instantiate Spurious dragon evm executor.
*
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor tangerineWhistle(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.tangerineWhistle(evmConfiguration));
public static EVMExecutor spuriousDragon(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.spuriousDragon(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -195,9 +287,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.ISTANBUL, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor istanbul(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.istanbul(evmConfiguration));
return evm(EvmSpecVersion.ISTANBUL, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Istanbul evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor istanbul(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.istanbul(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -209,9 +322,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.BERLIN, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor berlin(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.berlin(evmConfiguration));
return evm(EvmSpecVersion.BERLIN, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate berlin evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor berlin(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.berlin(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -223,9 +357,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.LONDON, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor london(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.london(evmConfiguration));
return evm(EvmSpecVersion.LONDON, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate London evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor london(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.london(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor;
@ -236,9 +391,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.PARIS, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor paris(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.paris(evmConfiguration));
return evm(EvmSpecVersion.PARIS, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Paris evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor paris(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.paris(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor;
@ -249,9 +425,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.SHANGHAI, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor shanghai(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.shanghai(evmConfiguration));
return evm(EvmSpecVersion.SHANGHAI, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Shanghai evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor shanghai(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.shanghai(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor;
@ -262,22 +459,125 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.CANCUN, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor cancun(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.cancun(evmConfiguration));
return evm(EvmSpecVersion.CANCUN, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Cancun evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor cancun(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.cancun(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Prague evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor prague(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.prague(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Osaka evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor osaka(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.osaka(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Bogota evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor bogota(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.bogota(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Future EIPs evm executor.
*
* @param evmConfiguration the evm configuration
* @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.FUTURE_EIPS, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor futureEips(final EvmConfiguration evmConfiguration) {
return evm(EvmSpecVersion.FUTURE_EIPS, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Future EIPs evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor futureEips(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.futureEips(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.futureEIPs(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Experimental EIPs evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor futureEIPs(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.cancun(evmConfiguration));
public static EVMExecutor experimentalEips(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor =
new EVMExecutor(MainnetEVMs.experimentalEips(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.futureEIPs(executor.evm.getGasCalculator());
return executor;
@ -347,10 +647,10 @@ public class EVMExecutor {
final ContractCreationProcessor ccp = thisContractCreationProcessor();
final MessageFrame initialMessageFrame =
MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL)
.type(messageFrameType)
.worldUpdater(worldUpdater.updater())
.initialGas(gas)
.contract(Address.ZERO)
.contract(contract)
.address(receiver)
.originator(sender)
.sender(sender)
@ -361,11 +661,12 @@ public class EVMExecutor {
.apparentValue(ethValue)
.code(code)
.blockValues(blockValues)
.completer(c -> {})
.miningBeneficiary(Address.ZERO)
.blockHashLookup(h -> null)
.miningBeneficiary(coinbase)
.blockHashLookup(blockHashLookup)
.accessListWarmAddresses(accessListWarmAddresses)
.accessListWarmStorage(accessListWarmStorage)
.versionedHashes(versionedHashes)
.completer(c -> {})
.build();
final Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack();
@ -389,8 +690,7 @@ public class EVMExecutor {
* @return the evm executor
*/
public EVMExecutor commitWorldState() {
this.commitWorldState = true;
return this;
return commitWorldState(true);
}
/**
@ -448,6 +748,32 @@ public class EVMExecutor {
return this;
}
/**
* Sets the address of the executing contract
*
* @param contract the contract
* @return the evm executor
*/
public EVMExecutor contract(final Address contract) {
this.contract = contract;
return this;
}
/**
* Sets the address of the coinbase aka mining beneficiary
*
* @param coinbase the coinbase
* @return the evm executor
*/
public EVMExecutor coinbase(final Address coinbase) {
this.coinbase = coinbase;
// EIP-3651
if (EvmSpecVersion.SHANGHAI.compareTo(evm.getEvmVersion()) <= 0) {
this.warmAddress(coinbase);
}
return this;
}
/**
* Sets Gas price GWei.
*
@ -503,6 +829,16 @@ public class EVMExecutor {
return this;
}
/**
* Sets Code.
*
* @param codeBytes the code bytes
* @return the evm executor
*/
public EVMExecutor code(final Bytes codeBytes) {
return code(codeBytes, Hash.hash(codeBytes));
}
/**
* Sets Code.
*
@ -526,6 +862,131 @@ public class EVMExecutor {
return this;
}
/**
* Sets the difficulty bytes on a SimpleBlockValues object
*
* @param difficulty the difficulty
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor difficulty(final Bytes difficulty) {
((SimpleBlockValues) this.blockValues).setDifficultyBytes(difficulty);
return this;
}
/**
* Sets the mix hash bytes on a SimpleBlockValues object
*
* @param mixHash the mix hash
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor mixHash(final Bytes32 mixHash) {
((SimpleBlockValues) this.blockValues).setMixHashOrPrevRandao(mixHash);
return this;
}
/**
* Sets the prev randao bytes on a SimpleBlockValues object
*
* @param prevRandao the prev randao
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor prevRandao(final Bytes32 prevRandao) {
((SimpleBlockValues) this.blockValues).setMixHashOrPrevRandao(prevRandao);
return this;
}
/**
* Sets the baseFee for the block, directly.
*
* @param baseFee the baseFee
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor baseFee(final Wei baseFee) {
return baseFee(Optional.ofNullable(baseFee));
}
/**
* Sets the baseFee for the block, as an Optional.
*
* @param baseFee the baseFee
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor baseFee(final Optional<Wei> baseFee) {
((SimpleBlockValues) this.blockValues).setBaseFee(baseFee);
return this;
}
/**
* Sets the block number for the block.
*
* @param number the block number
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor number(final long number) {
((SimpleBlockValues) this.blockValues).setNumber(number);
return this;
}
/**
* Sets the timestamp for the block.
*
* @param timestamp the block timestamp
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor timestamp(final long timestamp) {
((SimpleBlockValues) this.blockValues).setTimestamp(timestamp);
return this;
}
/**
* Sets the gas limit for the block.
*
* @param gasLimit the block gas limit
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor gasLimit(final long gasLimit) {
((SimpleBlockValues) this.blockValues).setGasLimit(gasLimit);
return this;
}
/**
* Sets the block hash lookup function
*
* @param blockHashLookup the block hash lookup function
* @return the evm executor
*/
public EVMExecutor blockHashLookup(final Function<Long, Hash> blockHashLookup) {
this.blockHashLookup = blockHashLookup;
return this;
}
/**
* Sets Version Hashes for blobs. The blobs themselves are not accessible.
*
* @param versionedHashes the versioned hashes
* @return the evm executor
*/
public EVMExecutor versionedHashes(final Optional<List<VersionedHash>> versionedHashes) {
this.versionedHashes = versionedHashes;
return this;
}
/**
* Sets Operation Tracer.
*
@ -664,4 +1125,33 @@ public class EVMExecutor {
this.contractCreationProcessor = contractCreationProcessor;
return this;
}
/**
* Sets the message frame type
*
* @param messageFrameType message frame type
* @return the builder
*/
public EVMExecutor messageFrameType(final MessageFrame.Type messageFrameType) {
this.messageFrameType = messageFrameType;
return this;
}
/**
* Returns the EVM version this executor is using
*
* @return the current EVM version
*/
public EvmSpecVersion getEVMVersion() {
return evm.getEvmVersion();
}
/**
* Returns the ChaindD this executor is using
*
* @return the current chain ID
*/
public Optional<Bytes> getChainId() {
return evm.getChainId();
}
}

@ -188,4 +188,21 @@ public class SimpleAccount implements MutableAccount {
public void becomeImmutable() {
mutable = false;
}
/**
* Push changes into the parent account, if one exists
*
* @return true if a parent account was updated, false if not (this indicates the account should
* be inserted into the parent contact).
*/
public boolean updateParent() {
if (parent instanceof SimpleAccount simpleAccount) {
simpleAccount.balance = balance;
simpleAccount.nonce = nonce;
simpleAccount.storage.putAll(storage);
return true;
} else {
return false;
}
}
}

@ -15,7 +15,105 @@
*/
package org.hyperledger.besu.evm.fluent;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.frame.BlockValues;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
/** A concrete BlockValues object that takes all the defaults */
public class SimpleBlockValues implements BlockValues {}
public class SimpleBlockValues implements BlockValues {
Bytes difficultyBytes = Bytes32.ZERO;
Bytes32 mixHashOrPrevRandao = Bytes32.ZERO;
Optional<Wei> baseFee = Optional.empty();
long number = 1;
long timestamp = 1;
long gasLimit = Long.MAX_VALUE;
@Override
public Bytes getDifficultyBytes() {
return difficultyBytes;
}
/**
* Sets the difficulty of the block
*
* @param difficultyBytes the difficulty
*/
public void setDifficultyBytes(final Bytes difficultyBytes) {
this.difficultyBytes = difficultyBytes;
}
@Override
public Bytes32 getMixHashOrPrevRandao() {
return mixHashOrPrevRandao;
}
/**
* sets the mix hash or prevRandao
*
* @param mixHashOrPrevRandao new mixHash or prevRandao
*/
public void setMixHashOrPrevRandao(final Bytes32 mixHashOrPrevRandao) {
this.mixHashOrPrevRandao = mixHashOrPrevRandao;
}
@Override
public Optional<Wei> getBaseFee() {
return baseFee;
}
/**
* Sets the base fee
*
* @param baseFee new base fee, or empty if not in a fee market fork.
*/
public void setBaseFee(final Optional<Wei> baseFee) {
this.baseFee = baseFee;
}
@Override
public long getNumber() {
return number;
}
/**
* Sets the block number
*
* @param number the block number
*/
public void setNumber(final long number) {
this.number = number;
}
@Override
public long getTimestamp() {
return timestamp;
}
/**
* Sets the block timestamp
*
* @param timestamp the timestamp, in seconds past the unix epoch
*/
public void setTimestamp(final long timestamp) {
this.timestamp = timestamp;
}
@Override
public long getGasLimit() {
return gasLimit;
}
/**
* Sets the gas limit
*
* @param gasLimit the gas limit for the block
*/
public void setGasLimit(final long gasLimit) {
this.gasLimit = gasLimit;
}
}

@ -66,6 +66,9 @@ public class SimpleWorld implements WorldUpdater {
@Override
public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) {
if (getAccount(address) != null) {
throw new IllegalStateException("Cannot create an account when one already exists");
}
SimpleAccount account = new SimpleAccount(address, nonce, balance);
accounts.put(address, account);
return account;
@ -73,13 +76,23 @@ public class SimpleWorld implements WorldUpdater {
@Override
public MutableAccount getAccount(final Address address) {
if (accounts.containsKey(address)) {
return accounts.get(address);
} else if (parent != null) {
return parent.getAccount(address);
} else {
return null;
SimpleAccount account = accounts.get(address);
if (account != null) {
return account;
}
Account parentAccount = parent == null ? null : parent.getAccount(address);
if (parentAccount != null) {
account =
new SimpleAccount(
parentAccount,
parentAccount.getAddress(),
parentAccount.getNonce(),
parentAccount.getBalance(),
parentAccount.getCode());
accounts.put(address, account);
return account;
}
return null;
}
@Override
@ -107,11 +120,16 @@ public class SimpleWorld implements WorldUpdater {
@Override
public void commit() {
parent.accounts.putAll(accounts);
accounts.forEach(
(address, account) -> {
if (!account.updateParent()) {
parent.accounts.put(address, account);
}
});
}
@Override
public Optional<WorldUpdater> parentUpdater() {
return Optional.empty();
return Optional.ofNullable(parent);
}
}

@ -247,15 +247,26 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
BigIntegerModularExponentiationPrecompiledContract.modulusLength(input);
final long exponentOffset =
clampedAdd(BigIntegerModularExponentiationPrecompiledContract.BASE_OFFSET, baseLength);
long multiplicationComplexity = (Math.max(modulusLength, baseLength) + 7L) / 8L;
multiplicationComplexity =
Words.clampedMultiply(multiplicationComplexity, multiplicationComplexity);
if (multiplicationComplexity == 0) {
return 200;
} else if (multiplicationComplexity > 0) {
long maxExponentLength = Long.MAX_VALUE / multiplicationComplexity * 3 / 8;
if (exponentLength > maxExponentLength) {
return Long.MAX_VALUE;
}
}
final long firstExponentBytesCap =
Math.min(exponentLength, ByzantiumGasCalculator.MAX_FIRST_EXPONENT_BYTES);
final BigInteger firstExpBytes =
BigIntegerModularExponentiationPrecompiledContract.extractParameter(
input, clampedToInt(exponentOffset), clampedToInt(firstExponentBytesCap));
final long adjustedExponentLength = adjustedExponentLength(exponentLength, firstExpBytes);
long multiplicationComplexity = (Math.max(modulusLength, baseLength) + 7L) / 8L;
multiplicationComplexity =
Words.clampedMultiply(multiplicationComplexity, multiplicationComplexity);
long gasRequirement =
clampedMultiply(multiplicationComplexity, Math.max(adjustedExponentLength, 1L));

@ -18,11 +18,15 @@ import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
/** The Chain id operation. */
public class ChainIdOperation extends AbstractFixedCostOperation {
/** The CHAINID Opcode number */
public static final int OPCODE = 0x46;
private final Bytes32 chainId;
/**
@ -32,10 +36,19 @@ public class ChainIdOperation extends AbstractFixedCostOperation {
* @param chainId the chain id
*/
public ChainIdOperation(final GasCalculator gasCalculator, final Bytes32 chainId) {
super(0x46, "CHAINID", 0, 1, gasCalculator, gasCalculator.getBaseTierGasCost());
super(OPCODE, "CHAINID", 0, 1, gasCalculator, gasCalculator.getBaseTierGasCost());
this.chainId = chainId;
}
/**
* Returns the chain ID this operation uses
*
* @return then chainID;
*/
public Bytes getChainId() {
return chainId;
}
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {

@ -61,7 +61,8 @@ public class SelfDestructOperation extends AbstractOperation {
frame.warmUpAddress(beneficiaryAddress) || gasCalculator().isPrecompile(beneficiaryAddress);
final Address originatorAddress = frame.getRecipientAddress();
final Wei originatorBalance = frame.getWorldUpdater().get(originatorAddress).getBalance();
final MutableAccount originatorAccount = frame.getWorldUpdater().getAccount(originatorAddress);
final Wei originatorBalance = originatorAccount.getBalance();
final long cost =
gasCalculator().selfDestructOperationGasCost(beneficiaryNullable, originatorBalance)
@ -75,7 +76,6 @@ public class SelfDestructOperation extends AbstractOperation {
}
// We passed preliminary checks, get mutable accounts.
final MutableAccount originatorAccount = frame.getWorldUpdater().getAccount(originatorAddress);
final MutableAccount beneficiaryAccount =
frame.getWorldUpdater().getOrCreate(beneficiaryAddress);
@ -86,8 +86,8 @@ public class SelfDestructOperation extends AbstractOperation {
// If we are actually destroying the originator (pre-Cancun or same-tx-create) we need to
// explicitly zero out the account balance (destroying ether/value if the originator is the
// beneficiary) as well as tag it for later self-destruct cleanup.
if (!eip6780Semantics || frame.wasCreatedInTransaction(originatorAddress)) {
frame.addSelfDestruct(originatorAddress);
if (!eip6780Semantics || frame.wasCreatedInTransaction(originatorAccount.getAddress())) {
frame.addSelfDestruct(originatorAccount.getAddress());
originatorAccount.setBalance(Wei.ZERO);
}

@ -151,12 +151,6 @@ public class MessageCallProcessor extends AbstractMessageProcessor {
final OperationTracer operationTracer) {
final long gasRequirement = contract.gasRequirement(frame.getInputData());
if (frame.getRemainingGas() < gasRequirement) {
LOG.trace(
"Not enough gas available for pre-compiled contract code {}: requiring "
+ "{} but only {} gas available",
contract,
gasRequirement,
frame.getRemainingGas());
frame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
} else {
@ -174,11 +168,6 @@ public class MessageCallProcessor extends AbstractMessageProcessor {
}
frame.setState(result.getState());
frame.setExceptionalHaltReason(result.getHaltReason());
LOG.trace(
"Precompiled contract {} {} (gasComsumed: {})",
contract.getName(),
result.getState(),
result.isRefundGas() ? 0L : gasRequirement);
}
}
}

@ -21,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.fluent.EVMExecutor;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import java.io.ByteArrayOutputStream;
@ -37,7 +36,7 @@ class StandardJsonTracerTest {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(baos);
var executor = EVMExecutor.istanbul(EvmConfiguration.DEFAULT);
var executor = EVMExecutor.evm(EvmSpecVersion.ISTANBUL);
StandardJsonTracer tracer = new StandardJsonTracer(out, true, true, true, false);
executor.tracer(tracer);
executor.gas(10_000_000_000L);
@ -82,7 +81,7 @@ class StandardJsonTracerTest {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(baos);
var executor = EVMExecutor.istanbul(EvmConfiguration.DEFAULT);
var executor = EVMExecutor.evm(EvmSpecVersion.ISTANBUL);
StandardJsonTracer tracer = new StandardJsonTracer(out, false, false, false, true);
executor.tracer(tracer);
executor.gas(10_000_000_000L);

@ -0,0 +1,229 @@
/*
* 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.fluent;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.OperationRegistry;
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.processor.MessageCallProcessor;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import com.google.common.collect.MultimapBuilder;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
class EVMExecutorTest {
@Test
void currentEVM() {
var subject = EVMExecutor.evm();
assertThat(subject.getEVMVersion()).isEqualTo(EvmSpecVersion.SHANGHAI);
}
@ParameterizedTest
@EnumSource(EvmSpecVersion.class)
void evmByRequest(final EvmSpecVersion version) {
var subject = EVMExecutor.evm(version);
assertThat(subject.getEVMVersion()).isEqualTo(version);
}
@ParameterizedTest
@EnumSource(EvmSpecVersion.class)
void evmWithChainIDByRequest(final EvmSpecVersion version) {
var subject = EVMExecutor.evm(version, BigInteger.TEN);
assertThat(subject.getEVMVersion()).isEqualTo(version);
if (EvmSpecVersion.ISTANBUL.compareTo(version) <= 0) {
assertThat(subject.getChainId()).map(Bytes::trimLeadingZeros).map(Bytes::toInt).contains(10);
} else {
assertThat(subject.getChainId()).isEmpty();
}
}
@ParameterizedTest
@EnumSource(EvmSpecVersion.class)
void evmWithChainIDByBytes(final EvmSpecVersion version) {
var subject = EVMExecutor.evm(version, Bytes.fromHexString("0xc4a1201d"));
assertThat(subject.getEVMVersion()).isEqualTo(version);
if (EvmSpecVersion.ISTANBUL.compareTo(version) <= 0) {
assertThat(subject.getChainId())
.map(Bytes::trimLeadingZeros)
.map(Bytes::toInt)
.contains(0xc4a1201d);
} else {
assertThat(subject.getChainId()).isEmpty();
}
}
@Test
void customEVM() {
var subject =
EVMExecutor.evm(
new EVM(
new OperationRegistry(),
new FrontierGasCalculator(),
EvmConfiguration.DEFAULT,
EvmSpecVersion.EXPERIMENTAL_EIPS));
assertThat(subject).isNotNull();
}
@Test
void nullEVM() {
assertThrows(NullPointerException.class, () -> EVMExecutor.evm((EVM) null));
}
@SuppressWarnings({"removal", "InlineMeInliner"})
@Test
void defaultChainIdAPIs() {
Bytes32 defaultChainId = Bytes32.leftPad(Bytes.of(1));
EVMExecutor istanbulEVM = EVMExecutor.istanbul(EvmConfiguration.DEFAULT);
assertThat(istanbulEVM.getChainId()).contains(defaultChainId);
EVMExecutor berlinEVM = EVMExecutor.berlin(EvmConfiguration.DEFAULT);
assertThat(berlinEVM.getChainId()).contains(defaultChainId);
EVMExecutor londonEVM = EVMExecutor.london(EvmConfiguration.DEFAULT);
assertThat(londonEVM.getChainId()).contains(defaultChainId);
EVMExecutor parisEVM = EVMExecutor.paris(EvmConfiguration.DEFAULT);
assertThat(parisEVM.getChainId()).contains(defaultChainId);
EVMExecutor shanghaiEVM = EVMExecutor.shanghai(EvmConfiguration.DEFAULT);
assertThat(shanghaiEVM.getChainId()).contains(defaultChainId);
EVMExecutor cancunEVM = EVMExecutor.cancun(EvmConfiguration.DEFAULT);
assertThat(cancunEVM.getChainId()).contains(defaultChainId);
EVMExecutor futureEipsVM = EVMExecutor.futureEips(EvmConfiguration.DEFAULT);
assertThat(futureEipsVM.getChainId()).contains(defaultChainId);
}
@Test
void executeCode() {
var result =
EVMExecutor.evm(EvmSpecVersion.SHANGHAI)
.worldUpdater(createSimpleWorld().updater())
.execute(
CodeFactory.createCode(Bytes.fromHexString("0x6001600255"), 1, false),
Bytes.EMPTY,
Wei.ZERO,
Address.ZERO);
assertThat(result).isNotNull();
}
@Test
void executeBytes() {
var result =
EVMExecutor.evm(EvmSpecVersion.SHANGHAI)
.worldUpdater(createSimpleWorld().updater())
.execute(Bytes.fromHexString("0x6001600255"), Bytes.EMPTY, Wei.ZERO, Address.ZERO);
assertThat(result).isNotNull();
}
@Test
void giantExecuteStack() {
SimpleWorld simpleWorld = createSimpleWorld();
var tracer = new StandardJsonTracer(System.out, false, true, true, false);
var result =
EVMExecutor.evm(EvmSpecVersion.SHANGHAI)
.messageFrameType(MessageFrame.Type.CONTRACT_CREATION)
.worldUpdater(simpleWorld.updater())
.tracer(tracer)
.contract(Address.fromHexString("0x100"))
.gas(15_000_000L)
.sender(Address.fromHexString("0x200"))
.receiver(Address.fromHexString("0x300"))
.coinbase(Address.fromHexString("0x400"))
.number(1)
.timestamp(9999)
.gasLimit(15_000_000)
.commitWorldState()
.gasPriceGWei(Wei.ONE)
.blobGasPrice(Wei.ONE)
.callData(Bytes.fromHexString("0x12345678"))
.ethValue(Wei.fromEth(1))
.code(CodeFactory.createCode(Bytes.fromHexString("0x6001600255"), 0, false))
.blockValues(new SimpleBlockValues())
.difficulty(Bytes.ofUnsignedLong(1L))
.mixHash(Bytes32.ZERO)
.baseFee(Wei.ONE)
.number(1)
.timestamp(100L)
.gasLimit(15_000_000L)
.blockHashLookup(number -> Hash.ZERO)
.versionedHashes(Optional.empty())
.precompileContractRegistry(new PrecompileContractRegistry())
.requireDeposit(false)
.initialNonce(42)
.contractValidationRules(List.of())
.forceCommitAddresses(List.of())
.warmAddress(Address.ZERO)
.accessListWarmStorage(
Address.ZERO, Bytes32.ZERO, Bytes32.leftPad(Bytes.ofUnsignedLong(2L)))
.messageCallProcessor(new MessageCallProcessor(null, null))
.contractCallProcessor(new ContractCreationProcessor(null, null, true, null, 1L))
.execute();
assertThat(result).isNotNull();
}
@Test
void anternateExecStack() {
SimpleWorld simpleWorld = createSimpleWorld();
var result =
EVMExecutor.evm(EvmSpecVersion.SHANGHAI)
.worldUpdater(simpleWorld.updater())
.messageFrameType(MessageFrame.Type.MESSAGE_CALL)
.code(Bytes.fromHexString("0x6001600255"))
.prevRandao(Bytes32.ZERO)
.accessListWarmAddresses(Set.of())
.accessListWarmStorage(MultimapBuilder.linkedHashKeys().arrayListValues().build())
.execute();
assertThat(result).isNotNull();
}
@NotNull
private static SimpleWorld createSimpleWorld() {
SimpleWorld simpleWorld = new SimpleWorld();
simpleWorld.createAccount(Address.fromHexString("0x0"), 1, Wei.fromEth(100));
simpleWorld.createAccount(Address.fromHexString("0x100"), 1, Wei.fromEth(100));
simpleWorld.createAccount(Address.fromHexString("0x200"), 1, Wei.fromEth(100));
simpleWorld.createAccount(Address.fromHexString("0x300"), 1, Wei.fromEth(100));
simpleWorld.createAccount(Address.fromHexString("0x400"), 1, Wei.fromEth(100));
return simpleWorld;
}
}

@ -96,11 +96,11 @@ public class SelfDestructOperationTest {
}
when(worldUpdater.getAccount(originatorAddress)).thenReturn(accountOriginator);
when(worldUpdater.get(originatorAddress)).thenReturn(accountOriginator);
if (!originatorAddress.equals(beneficiaryAddress)) {
when(worldUpdater.get(beneficiaryAddress)).thenReturn(accountBeneficiary);
}
when(worldUpdater.getOrCreate(beneficiaryAddress)).thenReturn(accountBeneficiary);
when(accountOriginator.getAddress()).thenReturn(originatorAddress);
when(accountOriginator.getBalance()).thenReturn(Wei.fromHexString(balanceHex));
final Operation.OperationResult operationResult = operation.execute(messageFrame, evm);

@ -26,9 +26,9 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.fluent.EVMExecutor;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -99,7 +99,7 @@ abstract class AbstractMessageProcessorTest<T extends AbstractMessageProcessor>
@Test
void shouldTraceContextEnterExitForEip3155Test() {
final EVMExecutor executor = EVMExecutor.shanghai(EvmConfiguration.DEFAULT);
final EVMExecutor executor = EVMExecutor.evm(EvmSpecVersion.SHANGHAI);
final ContextTracer contextTracer = new ContextTracer();
executor.tracer(contextTracer);

Loading…
Cancel
Save