From 13a934d6b8decfbc16e1c8076174cabe3f372a8f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 4 Oct 2023 19:35:36 -0600 Subject: [PATCH] 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 --- .../hyperledger/besu/datatypes/Address.java | 8 +- .../besu/evmtool/EvmToolCommand.java | 13 +- .../besu/evmtool/trace/coinbase-cold.json | 57 ++ .../besu/evmtool/trace/coinbase-warm.json | 57 ++ .../java/org/hyperledger/besu/evm/EVM.java | 25 + .../hyperledger/besu/evm/EvmSpecVersion.java | 24 +- .../org/hyperledger/besu/evm/MainnetEVMs.java | 39 +- .../besu/evm/fluent/EVMExecutor.java | 538 +++++++++++++++++- .../besu/evm/fluent/SimpleAccount.java | 17 + .../besu/evm/fluent/SimpleBlockValues.java | 100 +++- .../besu/evm/fluent/SimpleWorld.java | 34 +- .../gascalculator/BerlinGasCalculator.java | 17 +- .../besu/evm/operation/ChainIdOperation.java | 15 +- .../evm/operation/SelfDestructOperation.java | 8 +- .../evm/processor/MessageCallProcessor.java | 11 - .../besu/evm/StandardJsonTracerTest.java | 5 +- .../besu/evm/fluent/EVMExecutorTest.java | 229 ++++++++ .../operations/SelfDestructOperationTest.java | 2 +- .../AbstractMessageProcessorTest.java | 4 +- 19 files changed, 1130 insertions(+), 73 deletions(-) create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-cold.json create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-warm.json create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java index 0897617c83..5d1fad698f 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.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); + } } /** diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java index 0e2daf96eb..f910de3197 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java @@ -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 = "
", + description = "Coinbase for this invocation.") + private final Address coinbase = Address.fromHexString("0x00"); + @Option( names = {"--input"}, paramLabel = "", @@ -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 messageFrameStack = initialMessageFrame.getMessageFrameStack(); diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-cold.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-cold.json new file mode 100644 index 0000000000..406c2684e2 --- /dev/null +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-cold.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-warm.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-warm.json new file mode 100644 index 0000000000..9140a0268c --- /dev/null +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-warm.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java index 3f5d706d41..f31cec13f0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java @@ -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 getChainId() { + Operation op = operations.get(ChainIdOperation.OPCODE); + if (op instanceof ChainIdOperation chainIdOperation) { + return Optional.of(chainIdOperation.getChainId()); + } else { + return Optional.empty(); + } + } + /** * Run to halt. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java index bd53ba11d2..88b886bb21 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java @@ -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(); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 7884816ecf..0d9fa18b59 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -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); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java index 9921f56e64..d046c64778 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java @@ -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 blockHashLookup = h -> null; + private Optional> versionedHashes = Optional.empty(); private OperationTracer tracer = OperationTracer.NO_TRACING; private boolean requireDeposit = true; private List contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000), PrefixCodeRule.of()); - private long initialNonce = 0; + private long initialNonce = 1; private Collection
forceCommitAddresses = List.of(Address.fromHexString("0x03")); - private Set
accessListWarmAddresses = Set.of(); + private Set
accessListWarmAddresses = new HashSet<>(); private Multimap 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. + * + *

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 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 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 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> 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 getChainId() { + return evm.getChainId(); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java index b8a4db21ea..05ad66574f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java @@ -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; + } + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleBlockValues.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleBlockValues.java index f6c7c26a7d..8099e27bab 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleBlockValues.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleBlockValues.java @@ -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 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 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 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; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java index 6bc19ca0da..a9295568a6 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java @@ -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 parentUpdater() { - return Optional.empty(); + return Optional.ofNullable(parent); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java index c15de3270f..50a035eb84 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java @@ -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)); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ChainIdOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ChainIdOperation.java index 66918298fa..f807d74597 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ChainIdOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ChainIdOperation.java @@ -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) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java index 3f29fcea57..453dff9ce5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java @@ -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); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java b/evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java index 0c100e3340..1963bba8a5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java @@ -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); } } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/StandardJsonTracerTest.java b/evm/src/test/java/org/hyperledger/besu/evm/StandardJsonTracerTest.java index 9ba9ebf96a..9577298681 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/StandardJsonTracerTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/StandardJsonTracerTest.java @@ -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); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java new file mode 100644 index 0000000000..176f007098 --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java @@ -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; + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java index 6c178dda2a..913ffd92bc 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java @@ -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); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessorTest.java index 7932741b3d..598758fb75 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessorTest.java @@ -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 @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);