mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 22:07:59 -05:00
EIP-7692 "Mega" EOF Implementation (#7169)
A complete and up to date implementation of EIP-7692 EOF v.1. For genesis file activation use "PragueEOFTime", for references tests it activates as part of Prague. Signed-off-by: Danno Ferrin <danno@numisight.com>
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
- Improve the selection of the most profitable built block [#7174](https://github.com/hyperledger/besu/pull/7174)
|
||||
- Support for eth_maxPriorityFeePerGas [#5658](https://github.com/hyperledger/besu/issues/5658)
|
||||
- Enable continuous profiling with default setting [#7006](https://github.com/hyperledger/besu/pull/7006)
|
||||
- A full and up to date implementation of EOF for Prague [#7169](https://github.com/hyperledger/besu/pull/7169)
|
||||
|
||||
### Bug fixes
|
||||
- Make `eth_gasPrice` aware of the base fee market [#7102](https://github.com/hyperledger/besu/pull/7102)
|
||||
|
||||
@@ -1504,7 +1504,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
}
|
||||
|
||||
if (genesisConfigOptionsSupplier.get().getCancunTime().isPresent()
|
||||
|| genesisConfigOptionsSupplier.get().getPragueTime().isPresent()) {
|
||||
|| genesisConfigOptionsSupplier.get().getPragueTime().isPresent()
|
||||
|| genesisConfigOptionsSupplier.get().getPragueEOFTime().isPresent()) {
|
||||
if (kzgTrustedSetupFile != null) {
|
||||
KZGPointEvalPrecompiledContract.init(kzgTrustedSetupFile);
|
||||
} else {
|
||||
|
||||
@@ -249,6 +249,13 @@ public interface GenesisConfigOptions {
|
||||
*/
|
||||
OptionalLong getPragueTime();
|
||||
|
||||
/**
|
||||
* Gets Prague EOF time.
|
||||
*
|
||||
* @return the prague time
|
||||
*/
|
||||
OptionalLong getPragueEOFTime();
|
||||
|
||||
/**
|
||||
* Gets future eips time.
|
||||
*
|
||||
|
||||
@@ -298,6 +298,11 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
|
||||
return getOptionalLong("praguetime");
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalLong getPragueEOFTime() {
|
||||
return getOptionalLong("pragueeoftime");
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalLong getFutureEipsTime() {
|
||||
return getOptionalLong("futureeipstime");
|
||||
@@ -457,6 +462,7 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
|
||||
getShanghaiTime().ifPresent(l -> builder.put("shanghaiTime", l));
|
||||
getCancunTime().ifPresent(l -> builder.put("cancunTime", l));
|
||||
getPragueTime().ifPresent(l -> builder.put("pragueTime", l));
|
||||
getPragueEOFTime().ifPresent(l -> builder.put("pragueEOFTime", l));
|
||||
getTerminalBlockNumber().ifPresent(l -> builder.put("terminalBlockNumber", l));
|
||||
getTerminalBlockHash().ifPresent(h -> builder.put("terminalBlockHash", h.toHexString()));
|
||||
getFutureEipsTime().ifPresent(l -> builder.put("futureEipsTime", l));
|
||||
@@ -605,6 +611,7 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
|
||||
getShanghaiTime(),
|
||||
getCancunTime(),
|
||||
getPragueTime(),
|
||||
getPragueEOFTime(),
|
||||
getFutureEipsTime(),
|
||||
getExperimentalEipsTime());
|
||||
// when adding forks add an entry to ${REPO_ROOT}/config/src/test/resources/all_forks.json
|
||||
|
||||
@@ -49,6 +49,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
|
||||
private OptionalLong shanghaiTime = OptionalLong.empty();
|
||||
private OptionalLong cancunTime = OptionalLong.empty();
|
||||
private OptionalLong pragueTime = OptionalLong.empty();
|
||||
private OptionalLong pragueEOFTime = OptionalLong.empty();
|
||||
private OptionalLong futureEipsTime = OptionalLong.empty();
|
||||
private OptionalLong experimentalEipsTime = OptionalLong.empty();
|
||||
private OptionalLong terminalBlockNumber = OptionalLong.empty();
|
||||
@@ -242,6 +243,11 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
|
||||
return pragueTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalLong getPragueEOFTime() {
|
||||
return pragueEOFTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalLong getFutureEipsTime() {
|
||||
return futureEipsTime;
|
||||
@@ -635,6 +641,18 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* PragueEOF time.
|
||||
*
|
||||
* @param timestamp the timestamp
|
||||
* @return the stub genesis config options
|
||||
*/
|
||||
public StubGenesisConfigOptions pragueEOFTime(final long timestamp) {
|
||||
pragueTime = OptionalLong.of(timestamp);
|
||||
pragueEOFTime = pragueTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Future EIPs Time block.
|
||||
*
|
||||
|
||||
@@ -199,6 +199,13 @@ class GenesisConfigOptionsTest {
|
||||
assertThat(config.getPragueTime()).hasValue(1670470143);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetPragueEOFTime() {
|
||||
final GenesisConfigOptions config =
|
||||
fromConfigOptions(singletonMap("pragueEOFTime", 1670470143));
|
||||
assertThat(config.getPragueEOFTime()).hasValue(1670470143);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetFutureEipsTime() {
|
||||
final GenesisConfigOptions config = fromConfigOptions(singletonMap("futureEipsTime", 1337));
|
||||
@@ -232,6 +239,7 @@ class GenesisConfigOptionsTest {
|
||||
assertThat(config.getShanghaiTime()).isEmpty();
|
||||
assertThat(config.getCancunTime()).isEmpty();
|
||||
assertThat(config.getPragueTime()).isEmpty();
|
||||
assertThat(config.getPragueEOFTime()).isEmpty();
|
||||
assertThat(config.getFutureEipsTime()).isEmpty();
|
||||
assertThat(config.getExperimentalEipsTime()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -312,6 +312,14 @@ public final class GenesisState {
|
||||
if (pragueTimestamp.isPresent()) {
|
||||
return genesis.getTimestamp() >= pragueTimestamp.getAsLong();
|
||||
}
|
||||
return isPragueEOFAtGenesis(genesis);
|
||||
}
|
||||
|
||||
private static boolean isPragueEOFAtGenesis(final GenesisConfigFile genesis) {
|
||||
final OptionalLong pragueEOFTimestamp = genesis.getConfigOptions().getPragueEOFTime();
|
||||
if (pragueEOFTimestamp.isPresent()) {
|
||||
return genesis.getTimestamp() >= pragueEOFTimestamp.getAsLong();
|
||||
}
|
||||
return isFutureEipsTimeAtGenesis(genesis);
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,17 @@ public class MainnetProtocolSpecFactory {
|
||||
miningParameters);
|
||||
}
|
||||
|
||||
public ProtocolSpecBuilder pragueEOFDefinition(final GenesisConfigOptions genesisConfigOptions) {
|
||||
return MainnetProtocolSpecs.pragueEOFDefinition(
|
||||
chainId,
|
||||
contractSizeLimit,
|
||||
evmStackSize,
|
||||
isRevertReasonEnabled,
|
||||
genesisConfigOptions,
|
||||
evmConfiguration,
|
||||
miningParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "future" fork consists of EIPs that have been approved for Ethereum Mainnet but not
|
||||
* scheduled for a fork. This is also known as "Eligible For Inclusion" (EFI) or "Considered for
|
||||
|
||||
@@ -58,6 +58,7 @@ 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;
|
||||
import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
|
||||
@@ -735,9 +736,6 @@ public abstract class MainnetProtocolSpecs {
|
||||
final GenesisConfigOptions genesisConfigOptions,
|
||||
final EvmConfiguration evmConfiguration,
|
||||
final MiningParameters miningParameters) {
|
||||
final int contractSizeLimit =
|
||||
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
|
||||
final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE);
|
||||
|
||||
final Address depositContractAddress =
|
||||
genesisConfigOptions.getDepositContractAddress().orElse(DEFAULT_DEPOSIT_CONTRACT_ADDRESS);
|
||||
@@ -750,47 +748,64 @@ public abstract class MainnetProtocolSpecs {
|
||||
genesisConfigOptions,
|
||||
evmConfiguration,
|
||||
miningParameters)
|
||||
// EVM changes to support EOF EIPs (3670, 4200, 4750, 5450)
|
||||
// EIP-3074 AUTH and AUTCALL gas
|
||||
.gasCalculator(PragueGasCalculator::new)
|
||||
// EIP-3074 AUTH and AUTCALL
|
||||
.evmBuilder(
|
||||
(gasCalculator, jdCacheConfig) ->
|
||||
MainnetEVMs.prague(
|
||||
gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration))
|
||||
// change contract call creator to accept EOF code
|
||||
|
||||
// EIP-2537 BLS12-381 precompiles
|
||||
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague)
|
||||
|
||||
// EIP-7002 Withdrawls / EIP-6610 Deposits / EIP-7685 Requests
|
||||
.requestsValidator(pragueRequestsValidator(depositContractAddress))
|
||||
// EIP-7002 Withdrawls / EIP-6610 Deposits / EIP-7685 Requests
|
||||
.requestProcessorCoordinator(pragueRequestsProcessors(depositContractAddress))
|
||||
|
||||
// EIP-2935 Blockhash processor
|
||||
.blockHashProcessor(new PragueBlockHashProcessor())
|
||||
.name("Prague");
|
||||
}
|
||||
|
||||
static ProtocolSpecBuilder pragueEOFDefinition(
|
||||
final Optional<BigInteger> chainId,
|
||||
final OptionalInt configContractSizeLimit,
|
||||
final OptionalInt configStackSizeLimit,
|
||||
final boolean enableRevertReason,
|
||||
final GenesisConfigOptions genesisConfigOptions,
|
||||
final EvmConfiguration evmConfiguration,
|
||||
final MiningParameters miningParameters) {
|
||||
final int contractSizeLimit =
|
||||
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
|
||||
|
||||
return pragueDefinition(
|
||||
chainId,
|
||||
configContractSizeLimit,
|
||||
configStackSizeLimit,
|
||||
enableRevertReason,
|
||||
genesisConfigOptions,
|
||||
evmConfiguration,
|
||||
miningParameters)
|
||||
// EIP-7692 EOF v1 Gas calculator
|
||||
.gasCalculator(PragueEOFGasCalculator::new)
|
||||
// EIP-7692 EOF v1 EVM and opcodes
|
||||
.evmBuilder(
|
||||
(gasCalculator, jdCacheConfig) ->
|
||||
MainnetEVMs.pragueEOF(
|
||||
gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration))
|
||||
// EIP-7698 EOF v1 creation transaction
|
||||
.contractCreationProcessorBuilder(
|
||||
(gasCalculator, evm) ->
|
||||
new ContractCreationProcessor(
|
||||
gasCalculator,
|
||||
evm,
|
||||
true,
|
||||
List.of(
|
||||
MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)),
|
||||
List.of(MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1)),
|
||||
1,
|
||||
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
|
||||
// warm blockahsh contract
|
||||
.transactionProcessorBuilder(
|
||||
(gasCalculator,
|
||||
feeMarket,
|
||||
transactionValidator,
|
||||
contractCreationProcessor,
|
||||
messageCallProcessor) ->
|
||||
new MainnetTransactionProcessor(
|
||||
gasCalculator,
|
||||
transactionValidator,
|
||||
contractCreationProcessor,
|
||||
messageCallProcessor,
|
||||
true,
|
||||
true,
|
||||
stackSizeLimit,
|
||||
feeMarket,
|
||||
CoinbaseFeePriceCalculator.eip1559()))
|
||||
|
||||
// use prague precompiled contracts
|
||||
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague)
|
||||
.requestsValidator(pragueRequestsValidator(depositContractAddress))
|
||||
.requestProcessorCoordinator(pragueRequestsProcessors(depositContractAddress))
|
||||
.blockHashProcessor(new PragueBlockHashProcessor())
|
||||
.name("Prague");
|
||||
.name("PragueEOF");
|
||||
}
|
||||
|
||||
static ProtocolSpecBuilder futureEipsDefinition(
|
||||
@@ -803,7 +818,7 @@ public abstract class MainnetProtocolSpecs {
|
||||
final MiningParameters miningParameters) {
|
||||
final int contractSizeLimit =
|
||||
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
|
||||
return pragueDefinition(
|
||||
return pragueEOFDefinition(
|
||||
chainId,
|
||||
configContractSizeLimit,
|
||||
configStackSizeLimit,
|
||||
@@ -823,8 +838,7 @@ public abstract class MainnetProtocolSpecs {
|
||||
gasCalculator,
|
||||
evm,
|
||||
true,
|
||||
List.of(
|
||||
MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)),
|
||||
List.of(MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1)),
|
||||
1,
|
||||
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
|
||||
// use future configured precompiled contracts
|
||||
|
||||
@@ -32,8 +32,10 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
|
||||
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.account.MutableAccount;
|
||||
import org.hyperledger.besu.evm.code.CodeInvalid;
|
||||
import org.hyperledger.besu.evm.code.CodeV0;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
@@ -382,13 +384,14 @@ public class MainnetTransactionProcessor {
|
||||
Address.contractAddress(senderAddress, sender.getNonce() - 1L);
|
||||
|
||||
final Bytes initCodeBytes = transaction.getPayload();
|
||||
Code code = contractCreationProcessor.getCodeFromEVMForCreation(initCodeBytes);
|
||||
initialFrame =
|
||||
commonMessageFrameBuilder
|
||||
.type(MessageFrame.Type.CONTRACT_CREATION)
|
||||
.address(contractAddress)
|
||||
.contract(contractAddress)
|
||||
.inputData(Bytes.EMPTY)
|
||||
.code(contractCreationProcessor.getCodeFromEVMUncached(initCodeBytes))
|
||||
.inputData(initCodeBytes.slice(code.getSize()))
|
||||
.code(code)
|
||||
.build();
|
||||
} else {
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent") // isContractCall tests isPresent
|
||||
@@ -415,12 +418,17 @@ public class MainnetTransactionProcessor {
|
||||
} else {
|
||||
initialFrame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
|
||||
initialFrame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INVALID_CODE));
|
||||
validationResult =
|
||||
ValidationResult.invalid(
|
||||
TransactionInvalidReason.EOF_CODE_INVALID,
|
||||
((CodeInvalid) initialFrame.getCode()).getInvalidReason());
|
||||
}
|
||||
|
||||
if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
|
||||
worldUpdater.commit();
|
||||
} else {
|
||||
if (initialFrame.getExceptionalHaltReason().isPresent()) {
|
||||
if (initialFrame.getExceptionalHaltReason().isPresent()
|
||||
&& initialFrame.getCode().isValid()) {
|
||||
validationResult =
|
||||
ValidationResult.invalid(
|
||||
TransactionInvalidReason.EXECUTION_HALTED,
|
||||
|
||||
@@ -252,6 +252,7 @@ public class ProtocolScheduleBuilder {
|
||||
lastForkBlock = validateForkOrder("Shanghai", config.getShanghaiTime(), lastForkBlock);
|
||||
lastForkBlock = validateForkOrder("Cancun", config.getCancunTime(), lastForkBlock);
|
||||
lastForkBlock = validateForkOrder("Prague", config.getPragueTime(), lastForkBlock);
|
||||
lastForkBlock = validateForkOrder("PragueEOF", config.getPragueEOFTime(), lastForkBlock);
|
||||
lastForkBlock = validateForkOrder("FutureEips", config.getFutureEipsTime(), lastForkBlock);
|
||||
lastForkBlock =
|
||||
validateForkOrder("ExperimentalEips", config.getExperimentalEipsTime(), lastForkBlock);
|
||||
@@ -331,6 +332,7 @@ public class ProtocolScheduleBuilder {
|
||||
timestampMilestone(config.getShanghaiTime(), specFactory.shanghaiDefinition(config)),
|
||||
timestampMilestone(config.getCancunTime(), specFactory.cancunDefinition(config)),
|
||||
timestampMilestone(config.getPragueTime(), specFactory.pragueDefinition(config)),
|
||||
timestampMilestone(config.getPragueEOFTime(), specFactory.pragueEOFDefinition(config)),
|
||||
timestampMilestone(config.getFutureEipsTime(), specFactory.futureEipsDefinition(config)),
|
||||
timestampMilestone(
|
||||
config.getExperimentalEipsTime(), specFactory.experimentalEipsDefinition(config)),
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.mainnet.TransactionValidatorFactory;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.account.MutableAccount;
|
||||
import org.hyperledger.besu.evm.code.CodeV0;
|
||||
@@ -138,13 +139,14 @@ public class PrivateTransactionProcessor {
|
||||
privacyGroupId);
|
||||
|
||||
final Bytes initCodeBytes = transaction.getPayload();
|
||||
Code code = contractCreationProcessor.getCodeFromEVMForCreation(initCodeBytes);
|
||||
initialFrame =
|
||||
commonMessageFrameBuilder
|
||||
.type(MessageFrame.Type.CONTRACT_CREATION)
|
||||
.address(privateContractAddress)
|
||||
.contract(privateContractAddress)
|
||||
.inputData(Bytes.EMPTY)
|
||||
.code(contractCreationProcessor.getCodeFromEVMUncached(initCodeBytes))
|
||||
.inputData(initCodeBytes.slice(code.getSize()))
|
||||
.code(code)
|
||||
.build();
|
||||
} else {
|
||||
final Address to = transaction.getTo().get();
|
||||
|
||||
@@ -49,6 +49,7 @@ public enum TransactionInvalidReason {
|
||||
INVALID_BLOBS,
|
||||
PLUGIN_TX_POOL_VALIDATOR,
|
||||
EXECUTION_HALTED,
|
||||
EOF_CODE_INVALID,
|
||||
// Private Transaction Invalid Reasons
|
||||
PRIVATE_TRANSACTION_INVALID,
|
||||
PRIVATE_TRANSACTION_FAILED,
|
||||
|
||||
@@ -17,8 +17,9 @@ package org.hyperledger.besu.evmtool;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.hyperledger.besu.evmtool.CodeValidateSubCommand.COMMAND_NAME;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.code.CodeFactory;
|
||||
import org.hyperledger.besu.evm.code.CodeInvalid;
|
||||
import org.hyperledger.besu.evm.code.CodeV1Validation;
|
||||
import org.hyperledger.besu.evm.code.EOFLayout;
|
||||
import org.hyperledger.besu.util.LogConfigurator;
|
||||
|
||||
@@ -39,7 +40,7 @@ import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(
|
||||
name = COMMAND_NAME,
|
||||
description = "Execute an Ethereum State Test.",
|
||||
description = "Validates EVM code for fuzzing",
|
||||
mixinStandardHelpOptions = true,
|
||||
versionProvider = VersionProvider.class)
|
||||
public class CodeValidateSubCommand implements Runnable {
|
||||
@@ -109,24 +110,26 @@ public class CodeValidateSubCommand implements Runnable {
|
||||
} catch (RuntimeException re) {
|
||||
return "err: hex string -" + re + "\n";
|
||||
}
|
||||
if (codeBytes.size() == 0) {
|
||||
if (codeBytes.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var layout = EOFLayout.parseEOF(codeBytes);
|
||||
EOFLayout layout = EOFLayout.parseEOF(codeBytes);
|
||||
if (!layout.isValid()) {
|
||||
return "err: layout - " + layout.getInvalidReason() + "\n";
|
||||
return "err: layout - " + layout.invalidReason() + "\n";
|
||||
}
|
||||
|
||||
var code = CodeFactory.createCode(codeBytes, 1, true);
|
||||
if (!code.isValid()) {
|
||||
return "err: " + ((CodeInvalid) code).getInvalidReason() + "\n";
|
||||
String error = CodeV1Validation.validate(layout);
|
||||
if (error != null) {
|
||||
return "err: " + error + "\n";
|
||||
}
|
||||
|
||||
Code code = CodeFactory.createCode(codeBytes, 1);
|
||||
|
||||
return "OK "
|
||||
+ IntStream.range(0, code.getCodeSectionCount())
|
||||
.mapToObj(code::getCodeSection)
|
||||
.map(cs -> layout.getContainer().slice(cs.getEntryPoint(), cs.getLength()))
|
||||
.map(cs -> layout.container().slice(cs.getEntryPoint(), cs.getLength()))
|
||||
.map(Bytes::toUnprefixedHexString)
|
||||
.collect(Collectors.joining(","))
|
||||
+ "\n";
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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.evmtool;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec.TestResult.failed;
|
||||
import static org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec.TestResult.passed;
|
||||
import static org.hyperledger.besu.evmtool.EOFTestSubCommand.COMMAND_NAME;
|
||||
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec;
|
||||
import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec.TestResult;
|
||||
import org.hyperledger.besu.evm.EvmSpecVersion;
|
||||
import org.hyperledger.besu.evm.code.CodeFactory;
|
||||
import org.hyperledger.besu.evm.code.CodeInvalid;
|
||||
import org.hyperledger.besu.evm.code.CodeV1;
|
||||
import org.hyperledger.besu.evm.code.CodeV1Validation;
|
||||
import org.hyperledger.besu.evm.code.EOFLayout;
|
||||
import org.hyperledger.besu.util.LogConfigurator;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(
|
||||
name = COMMAND_NAME,
|
||||
description = "Runs EOF validation reference tests",
|
||||
mixinStandardHelpOptions = true,
|
||||
versionProvider = VersionProvider.class)
|
||||
public class EOFTestSubCommand implements Runnable {
|
||||
public static final String COMMAND_NAME = "eof-test";
|
||||
@CommandLine.ParentCommand private final EvmToolCommand parentCommand;
|
||||
|
||||
// picocli does it magically
|
||||
@CommandLine.Parameters private final List<Path> eofTestFiles = new ArrayList<>();
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--fork-name"},
|
||||
description = "Limit execution to one fork.")
|
||||
private String forkName = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--test-name"},
|
||||
description = "Limit execution to one test.")
|
||||
private String testVectorName = null;
|
||||
|
||||
public EOFTestSubCommand() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public EOFTestSubCommand(final EvmToolCommand parentCommand) {
|
||||
this.parentCommand = parentCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
LogConfigurator.setLevel("", "OFF");
|
||||
// presume ethereum mainnet for reference and EOF tests
|
||||
SignatureAlgorithmFactory.setDefaultInstance();
|
||||
final ObjectMapper eofTestMapper = JsonUtils.createObjectMapper();
|
||||
|
||||
final JavaType javaType =
|
||||
eofTestMapper
|
||||
.getTypeFactory()
|
||||
.constructParametricType(Map.class, String.class, EOFTestCaseSpec.class);
|
||||
try {
|
||||
if (eofTestFiles.isEmpty()) {
|
||||
// if no EOF tests were specified use standard input to get filenames
|
||||
final BufferedReader in =
|
||||
new BufferedReader(new InputStreamReader(parentCommand.in, UTF_8));
|
||||
while (true) {
|
||||
final String fileName = in.readLine();
|
||||
if (fileName == null) {
|
||||
// reached end of file. Stop the loop.
|
||||
break;
|
||||
}
|
||||
final File file = new File(fileName);
|
||||
if (file.isFile()) {
|
||||
final Map<String, EOFTestCaseSpec> eofTests = eofTestMapper.readValue(file, javaType);
|
||||
executeEOFTest(file.toString(), eofTests);
|
||||
} else {
|
||||
parentCommand.out.println("File not found: " + fileName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (final Path eofTestFile : eofTestFiles) {
|
||||
final Map<String, EOFTestCaseSpec> eofTests;
|
||||
if ("stdin".equals(eofTestFile.toString())) {
|
||||
eofTests = eofTestMapper.readValue(parentCommand.in, javaType);
|
||||
} else {
|
||||
eofTests = eofTestMapper.readValue(eofTestFile.toFile(), javaType);
|
||||
}
|
||||
executeEOFTest(eofTestFile.toString(), eofTests);
|
||||
}
|
||||
}
|
||||
} catch (final JsonProcessingException jpe) {
|
||||
parentCommand.out.println("File content error: " + jpe);
|
||||
} catch (final IOException e) {
|
||||
System.err.println("Unable to read EOF test file");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
record TestExecutionResult(
|
||||
String fileName,
|
||||
String group,
|
||||
String name,
|
||||
String fork,
|
||||
boolean pass,
|
||||
String expectedError,
|
||||
String actualError) {}
|
||||
|
||||
private void executeEOFTest(final String fileName, final Map<String, EOFTestCaseSpec> eofTests) {
|
||||
List<TestExecutionResult> results = new ArrayList<>();
|
||||
|
||||
for (var testGroup : eofTests.entrySet()) {
|
||||
String groupName = testGroup.getKey();
|
||||
for (var testVector : testGroup.getValue().getVector().entrySet()) {
|
||||
String testName = testVector.getKey();
|
||||
if (testVectorName != null && !testVectorName.equals(testName)) {
|
||||
continue;
|
||||
}
|
||||
String code = testVector.getValue().code();
|
||||
for (var testResult : testVector.getValue().results().entrySet()) {
|
||||
String expectedForkName = testResult.getKey();
|
||||
if (forkName != null && !forkName.equals(expectedForkName)) {
|
||||
continue;
|
||||
}
|
||||
TestResult expectedResult = testResult.getValue();
|
||||
EvmSpecVersion evmVersion = EvmSpecVersion.fromName(expectedForkName);
|
||||
if (evmVersion == null) {
|
||||
results.add(
|
||||
new TestExecutionResult(
|
||||
fileName,
|
||||
groupName,
|
||||
testName,
|
||||
expectedForkName,
|
||||
false,
|
||||
"Valid fork name",
|
||||
"Unknown fork: " + expectedForkName));
|
||||
|
||||
continue;
|
||||
}
|
||||
TestResult actualResult;
|
||||
if (evmVersion.ordinal() < EvmSpecVersion.PRAGUE_EOF.ordinal()) {
|
||||
actualResult = failed("EOF_InvalidCode");
|
||||
} else {
|
||||
actualResult = considerCode(code);
|
||||
}
|
||||
results.add(
|
||||
new TestExecutionResult(
|
||||
fileName,
|
||||
groupName,
|
||||
testName,
|
||||
expectedForkName,
|
||||
actualResult.result() == expectedResult.result(),
|
||||
expectedResult.exception(),
|
||||
actualResult.exception()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (TestExecutionResult result : results) {
|
||||
try {
|
||||
parentCommand.out.println(JsonUtils.createObjectMapper().writeValueAsString(result));
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace(parentCommand.out);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TestResult considerCode(final String hexCode) {
|
||||
Bytes codeBytes;
|
||||
try {
|
||||
codeBytes =
|
||||
Bytes.fromHexString(
|
||||
hexCode.replaceAll("(^|\n)#[^\n]*($|\n)", "").replaceAll("[^0-9A-Za-z]", ""));
|
||||
} catch (RuntimeException re) {
|
||||
return failed(re.getMessage());
|
||||
}
|
||||
if (codeBytes.isEmpty()) {
|
||||
return passed();
|
||||
}
|
||||
|
||||
var layout = EOFLayout.parseEOF(codeBytes);
|
||||
if (!layout.isValid()) {
|
||||
return failed("layout - " + layout.invalidReason());
|
||||
}
|
||||
|
||||
var code = CodeFactory.createCode(codeBytes, 1);
|
||||
if (!code.isValid()) {
|
||||
return failed("validate " + ((CodeInvalid) code).getInvalidReason());
|
||||
}
|
||||
if (code instanceof CodeV1 codeV1) {
|
||||
var result = CodeV1Validation.validate(codeV1.getEofLayout());
|
||||
if (result != null) {
|
||||
return (failed("deep validate error: " + result));
|
||||
}
|
||||
}
|
||||
|
||||
return passed();
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,8 @@ import picocli.CommandLine.Option;
|
||||
BenchmarkSubCommand.class,
|
||||
B11rSubCommand.class,
|
||||
CodeValidateSubCommand.class,
|
||||
EOFTestSubCommand.class,
|
||||
PrettyPrintSubCommand.class,
|
||||
StateTestSubCommand.class,
|
||||
T8nSubCommand.class,
|
||||
T8nServerSubCommand.class
|
||||
@@ -140,6 +142,11 @@ public class EvmToolCommand implements Runnable {
|
||||
description = "Receiving address for this invocation.")
|
||||
private final Address receiver = Address.ZERO;
|
||||
|
||||
@Option(
|
||||
names = {"--create"},
|
||||
description = "Run call should be a create instead of a call operation.")
|
||||
private final Boolean createTransaction = false;
|
||||
|
||||
@Option(
|
||||
names = {"--contract"},
|
||||
paramLabel = "<address>",
|
||||
@@ -340,7 +347,7 @@ public class EvmToolCommand implements Runnable {
|
||||
.nonce(0)
|
||||
.gasPrice(Wei.ZERO)
|
||||
.gasLimit(Long.MAX_VALUE)
|
||||
.to(receiver)
|
||||
.to(createTransaction ? null : receiver)
|
||||
.value(Wei.ZERO)
|
||||
.payload(callData)
|
||||
.sender(sender)
|
||||
@@ -361,10 +368,10 @@ public class EvmToolCommand implements Runnable {
|
||||
}
|
||||
|
||||
final EVM evm = protocolSpec.getEvm();
|
||||
if (codeBytes.isEmpty()) {
|
||||
if (codeBytes.isEmpty() && !createTransaction) {
|
||||
codeBytes = component.getWorldState().get(receiver).getCode();
|
||||
}
|
||||
Code code = evm.getCode(Hash.hash(codeBytes), codeBytes);
|
||||
Code code = evm.getCodeForCreation(codeBytes);
|
||||
if (!code.isValid()) {
|
||||
out.println(((CodeInvalid) code).getInvalidReason());
|
||||
return;
|
||||
@@ -381,7 +388,9 @@ public class EvmToolCommand implements Runnable {
|
||||
|
||||
WorldUpdater updater = component.getWorldUpdater();
|
||||
updater.getOrCreate(sender);
|
||||
updater.getOrCreate(receiver);
|
||||
if (!createTransaction) {
|
||||
updater.getOrCreate(receiver);
|
||||
}
|
||||
var contractAccount = updater.getOrCreate(contract);
|
||||
contractAccount.setCode(codeBytes);
|
||||
|
||||
@@ -412,18 +421,23 @@ public class EvmToolCommand implements Runnable {
|
||||
.baseFee(component.getBlockchain().getChainHeadHeader().getBaseFee().orElse(null))
|
||||
.buildBlockHeader();
|
||||
|
||||
Address contractAddress =
|
||||
createTransaction ? Address.contractAddress(receiver, 0) : receiver;
|
||||
MessageFrame initialMessageFrame =
|
||||
MessageFrame.builder()
|
||||
.type(MessageFrame.Type.MESSAGE_CALL)
|
||||
.type(
|
||||
createTransaction
|
||||
? MessageFrame.Type.CONTRACT_CREATION
|
||||
: MessageFrame.Type.MESSAGE_CALL)
|
||||
.worldUpdater(updater.updater())
|
||||
.initialGas(txGas)
|
||||
.contract(Address.ZERO)
|
||||
.address(receiver)
|
||||
.contract(contractAddress)
|
||||
.address(contractAddress)
|
||||
.originator(sender)
|
||||
.sender(sender)
|
||||
.gasPrice(gasPriceGWei)
|
||||
.blobGasPrice(blobGasPrice)
|
||||
.inputData(callData)
|
||||
.inputData(createTransaction ? codeBytes.slice(code.getSize()) : callData)
|
||||
.value(ethValue)
|
||||
.apparentValue(ethValue)
|
||||
.code(code)
|
||||
|
||||
@@ -116,6 +116,9 @@ class MainnetGenesisFileModule extends GenesisFileModule {
|
||||
Map.entry(
|
||||
"prague",
|
||||
createSchedule(new StubGenesisConfigOptions().pragueTime(0).baseFeePerGas(0x0a))),
|
||||
Map.entry(
|
||||
"pragueeof",
|
||||
createSchedule(new StubGenesisConfigOptions().pragueEOFTime(0).baseFeePerGas(0x0a))),
|
||||
Map.entry(
|
||||
"futureeips",
|
||||
createSchedule(new StubGenesisConfigOptions().futureEipsTime(0).baseFeePerGas(0x0a))),
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.evmtool;
|
||||
|
||||
import static org.hyperledger.besu.evmtool.PrettyPrintSubCommand.COMMAND_NAME;
|
||||
|
||||
import org.hyperledger.besu.evm.code.CodeV1Validation;
|
||||
import org.hyperledger.besu.evm.code.EOFLayout;
|
||||
import org.hyperledger.besu.util.LogConfigurator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(
|
||||
name = COMMAND_NAME,
|
||||
description = "Pretty Prints EOF Code",
|
||||
mixinStandardHelpOptions = true,
|
||||
versionProvider = VersionProvider.class)
|
||||
public class PrettyPrintSubCommand implements Runnable {
|
||||
public static final String COMMAND_NAME = "pretty-print";
|
||||
@CommandLine.ParentCommand private final EvmToolCommand parentCommand;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"-f", "--force"},
|
||||
description = "Always print well formated code, even if there is an error",
|
||||
paramLabel = "<boolean>")
|
||||
private final Boolean force = false;
|
||||
|
||||
// picocli does it magically
|
||||
@CommandLine.Parameters private final List<String> codeList = new ArrayList<>();
|
||||
|
||||
public PrettyPrintSubCommand() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public PrettyPrintSubCommand(final EvmToolCommand parentCommand) {
|
||||
this.parentCommand = parentCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
LogConfigurator.setLevel("", "OFF");
|
||||
|
||||
for (var hexCode : codeList) {
|
||||
Bytes container = Bytes.fromHexString(hexCode);
|
||||
if (container.get(0) != ((byte) 0xef) && container.get(1) != 0) {
|
||||
parentCommand.out.println(
|
||||
"Pretty printing of legacy EVM is not supported. Patches welcome!");
|
||||
|
||||
} else {
|
||||
EOFLayout layout = EOFLayout.parseEOF(container);
|
||||
if (layout.isValid()) {
|
||||
String validation = CodeV1Validation.validate(layout);
|
||||
if (validation == null || force) {
|
||||
layout.prettyPrint(parentCommand.out);
|
||||
}
|
||||
if (validation != null) {
|
||||
parentCommand.out.println("EOF code is invalid - " + validation);
|
||||
}
|
||||
} else {
|
||||
parentCommand.out.println("EOF layout is invalid - " + layout.invalidReason());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,7 +308,9 @@ public class StateTestSubCommand implements Runnable {
|
||||
"validationError",
|
||||
"Exception '" + spec.getExpectException() + "' was expected but did not occur");
|
||||
}
|
||||
|
||||
if (!result.getValidationResult().isValid()) {
|
||||
summaryLine.put("error", result.getValidationResult().getErrorMessage());
|
||||
}
|
||||
if (parentCommand.showJsonAlloc) {
|
||||
EvmToolCommand.dumpWorldState(worldState, parentCommand.out);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ 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;
|
||||
import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator;
|
||||
import org.hyperledger.besu.evm.precompile.PrecompiledContract;
|
||||
@@ -131,6 +132,8 @@ public abstract class BenchmarkExecutor {
|
||||
return switch (EvmSpecVersion.valueOf(fork.toUpperCase(Locale.ROOT))) {
|
||||
case HOMESTEAD -> new HomesteadGasCalculator();
|
||||
case FRONTIER -> new FrontierGasCalculator();
|
||||
case TANGERINE_WHISTLE -> null;
|
||||
case SPURIOUS_DRAGON -> null;
|
||||
case BYZANTIUM -> new ByzantiumGasCalculator();
|
||||
case CONSTANTINOPLE -> new ConstantinopleGasCalculator();
|
||||
case PETERSBURG -> new PetersburgGasCalculator();
|
||||
@@ -139,7 +142,9 @@ public abstract class BenchmarkExecutor {
|
||||
case LONDON, PARIS -> new LondonGasCalculator();
|
||||
case SHANGHAI -> new ShanghaiGasCalculator();
|
||||
case CANCUN -> new CancunGasCalculator();
|
||||
default -> new PragueGasCalculator();
|
||||
case PRAGUE -> new PragueGasCalculator();
|
||||
case PRAGUE_EOF, OSAKA, AMSTERDAM, BOGOTA, POLIS, BANGKOK, FUTURE_EIPS, EXPERIMENTAL_EIPS ->
|
||||
new PragueEOFGasCalculator();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,24 +24,24 @@ import java.io.PrintStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import picocli.CommandLine;
|
||||
|
||||
public class CodeValidationSubCommandTest {
|
||||
class CodeValidationSubCommandTest {
|
||||
|
||||
static final String CODE_STOP_ONLY = "0xef0001 010004 020001-0001 030000 00 00000000 00";
|
||||
static final String CODE_RETF_ONLY = "0xef0001 010004 020001-0001 030000 00 00000000 e4";
|
||||
static final String CODE_BAD_MAGIC = "0xefffff 010004 020001-0001 030000 00 00000000 e4";
|
||||
static final String CODE_STOP_ONLY = "0xef0001 010004 020001-0001 040000 00 00800000 00";
|
||||
static final String CODE_RETURN_ONLY = "0xef0001 010004 020001-0003 040000 00 00800002 5f5ff3";
|
||||
static final String CODE_BAD_MAGIC = "0xefffff 010004 020001-0001 040000 00 00800000 e4";
|
||||
static final String CODE_INTERIOR_COMMENTS =
|
||||
"""
|
||||
0xef0001 010008 020002-000c-0002 030000 00
|
||||
0xef0001 010008 020002-0009-0002 040000 00
|
||||
# 7 inputs 1 output,
|
||||
00000007-07010007
|
||||
59-59-59-59-59-59-59-e30001-50-e4
|
||||
00800004-04010004
|
||||
59-59-59-59-e30001-50-00
|
||||
# No immediate data
|
||||
f1-e4""";
|
||||
f8-e4""";
|
||||
static final String CODE_MULTIPLE =
|
||||
CODE_STOP_ONLY + "\n" + CODE_BAD_MAGIC + "\n" + CODE_RETF_ONLY + "\n";
|
||||
CODE_STOP_ONLY + "\n" + CODE_BAD_MAGIC + "\n" + CODE_RETURN_ONLY + "\n";
|
||||
|
||||
@Test
|
||||
public void testSingleValidViaInput() {
|
||||
void testSingleValidViaInput() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(CODE_STOP_ONLY.getBytes(UTF_8));
|
||||
final CodeValidateSubCommand codeValidateSubCommand =
|
||||
@@ -51,7 +51,7 @@ public class CodeValidationSubCommandTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleInvalidViaInput() {
|
||||
void testSingleInvalidViaInput() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(CODE_BAD_MAGIC.getBytes(UTF_8));
|
||||
final CodeValidateSubCommand codeValidateSubCommand =
|
||||
@@ -61,7 +61,7 @@ public class CodeValidationSubCommandTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleViaInput() {
|
||||
void testMultipleViaInput() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(CODE_MULTIPLE.getBytes(UTF_8));
|
||||
final CodeValidateSubCommand codeValidateSubCommand =
|
||||
@@ -72,12 +72,12 @@ public class CodeValidationSubCommandTest {
|
||||
"""
|
||||
OK 00
|
||||
err: layout - EOF header byte 1 incorrect
|
||||
OK e4
|
||||
OK 5f5ff3
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleValidViaCli() {
|
||||
void testSingleValidViaCli() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
|
||||
final CodeValidateSubCommand codeValidateSubCommand =
|
||||
@@ -89,7 +89,7 @@ public class CodeValidationSubCommandTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleInvalidViaCli() {
|
||||
void testSingleInvalidViaCli() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
|
||||
final CodeValidateSubCommand codeValidateSubCommand =
|
||||
@@ -101,37 +101,37 @@ public class CodeValidationSubCommandTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleViaCli() {
|
||||
void testMultipleViaCli() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
|
||||
final CodeValidateSubCommand codeValidateSubCommand =
|
||||
new CodeValidateSubCommand(bais, new PrintStream(baos));
|
||||
final CommandLine cmd = new CommandLine(codeValidateSubCommand);
|
||||
cmd.parseArgs(CODE_STOP_ONLY, CODE_BAD_MAGIC, CODE_RETF_ONLY);
|
||||
cmd.parseArgs(CODE_STOP_ONLY, CODE_BAD_MAGIC, CODE_RETURN_ONLY);
|
||||
codeValidateSubCommand.run();
|
||||
assertThat(baos.toString(UTF_8))
|
||||
.contains(
|
||||
"""
|
||||
OK 00
|
||||
err: layout - EOF header byte 1 incorrect
|
||||
OK e4
|
||||
OK 5f5ff3
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCliEclipsesInput() {
|
||||
void testCliEclipsesInput() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(CODE_STOP_ONLY.getBytes(UTF_8));
|
||||
final CodeValidateSubCommand codeValidateSubCommand =
|
||||
new CodeValidateSubCommand(bais, new PrintStream(baos));
|
||||
final CommandLine cmd = new CommandLine(codeValidateSubCommand);
|
||||
cmd.parseArgs(CODE_RETF_ONLY);
|
||||
cmd.parseArgs(CODE_RETURN_ONLY);
|
||||
codeValidateSubCommand.run();
|
||||
assertThat(baos.toString(UTF_8)).contains("OK e4\n");
|
||||
assertThat(baos.toString(UTF_8)).contains("OK 5f5ff3\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInteriorCommentsSkipped() {
|
||||
void testInteriorCommentsSkipped() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
|
||||
final CodeValidateSubCommand codeValidateSubCommand =
|
||||
@@ -139,11 +139,11 @@ public class CodeValidationSubCommandTest {
|
||||
final CommandLine cmd = new CommandLine(codeValidateSubCommand);
|
||||
cmd.parseArgs(CODE_INTERIOR_COMMENTS);
|
||||
codeValidateSubCommand.run();
|
||||
assertThat(baos.toString(UTF_8)).contains("OK 59595959595959e3000150e4,f1e4\n");
|
||||
assertThat(baos.toString(UTF_8)).contains("OK 59595959e300015000,f8e4\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlankLinesAndCommentsSkipped() {
|
||||
void testBlankLinesAndCommentsSkipped() {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ByteArrayInputStream bais =
|
||||
new ByteArrayInputStream(("# comment\n\n#blank line\n\n" + CODE_MULTIPLE).getBytes(UTF_8));
|
||||
@@ -155,7 +155,7 @@ public class CodeValidationSubCommandTest {
|
||||
"""
|
||||
OK 00
|
||||
err: layout - EOF header byte 1 incorrect
|
||||
OK e4
|
||||
OK 5f5ff3
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ public class EvmToolSpecTests {
|
||||
return findSpecFiles(new String[] {"b11r"});
|
||||
}
|
||||
|
||||
public static Object[][] prettyPrintTests() {
|
||||
return findSpecFiles(new String[] {"pretty-print"});
|
||||
}
|
||||
|
||||
public static Object[][] stateTestTests() {
|
||||
return findSpecFiles(new String[] {"state-test"});
|
||||
}
|
||||
@@ -110,7 +114,7 @@ public class EvmToolSpecTests {
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource({"b11rTests", "stateTestTests", "t8nTests", "traceTests"})
|
||||
@MethodSource({"b11rTests", "prettyPrintTests", "stateTestTests", "t8nTests", "traceTests"})
|
||||
void testBySpec(
|
||||
final String file,
|
||||
final JsonNode cliNode,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"cli": [
|
||||
"pretty-print",
|
||||
"0xEF0001010004020001001304000000008000026000e20200030000fff65b5b00600160015500"
|
||||
],
|
||||
"stdin": "",
|
||||
"stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0 - in=0 out=non-returning height=2\n 6000 # [0] PUSH1(0)\ne20200030000fff6 # [2] RJUMPV(3,0,-10)\n 5b # [10] NOOP\n 5b # [11] NOOP\n 00 # [12] STOP\n 6001 # [13] PUSH1(1)\n 6001 # [15] PUSH1(1)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Data section (empty)\n"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"cli": [
|
||||
"pretty-print",
|
||||
"0xef000101000402000100130300010043040000000080000436600060003736600060006000ec0060005500ef0001010004020001000b03000100200400000000800003366000600037366000ee00ef0001010004020001000d0400400000800002d10000600055d1002060015500"
|
||||
],
|
||||
"stdin": "",
|
||||
"stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n030001 # Total subcontainers ( 1 )\n 0043 # Sub container 0, 67 byte\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0004 # max stack: 4\n # Code section 0 - in=0 out=non-returning height=4\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n 6000 # [9] PUSH1(0)\n 6000 # [11] PUSH1(0)\n ec00 # [13] EOFCREATE(0)\n 6000 # [15] PUSH1(0)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Subcontainer 0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000b # Code section 0 , 11 bytes\n 030001 # Total subcontainers ( 1 )\n 0020 # Sub container 0, 32 byte\n 040000 # Data section length( 0 ) \n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0003 # max stack: 3\n # Code section 0 - in=0 out=non-returning height=3\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n ee00 # [9] RETURNCONTRACT(0)\n # Subcontainer 0.0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000d # Code section 0 , 13 bytes\n 040040 # Data section length( 64 ) (actual size 0) \n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0 - in=0 out=non-returning height=2\n d10000 # [0] DATALOADN(0x0000)\n 6000 # [3] PUSH1(0)\n 55 # [5] SSTORE\n d10020 # [6] DATALOADN(0x0020)\n 6001 # [9] PUSH1(1)\n 55 # [11] SSTORE\n 00 # [12] STOP\n # Data section (empty)\n # Subcontainer 0.0 ends\n # Data section (empty)\n # Subcontainer 0 ends\n # Data section (empty)\n"
|
||||
}
|
||||
@@ -99,6 +99,6 @@
|
||||
{"pc":81,"op":72,"gas":"0x79bc22","gasCost":"0x2","memSize":0,"stack":["0x0","0x1","0x1","0x2","0x2","0xffff","0x1f4","0x78859e5b97166c486532b1595a673e9f9073643f1b519c6f18511b9913","0x2","0x389","0x0","0x0","0x1","0x0","0x3e3d6d5ff042148d326c1898713a76759ca273","0x44852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d","0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b"],"depth":1,"refund":0,"opName":"BASEFEE"},
|
||||
{"pc":82,"op":8,"gas":"0x79bc20","gasCost":"0x8","memSize":0,"stack":["0x0","0x1","0x1","0x2","0x2","0xffff","0x1f4","0x78859e5b97166c486532b1595a673e9f9073643f1b519c6f18511b9913","0x2","0x389","0x0","0x0","0x1","0x0","0x3e3d6d5ff042148d326c1898713a76759ca273","0x44852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d","0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b","0x10"],"depth":1,"refund":0,"opName":"ADDMOD"},
|
||||
{"pc":83,"op":62,"gas":"0x79bc18","gasCost":"0x0","memSize":0,"stack":["0x0","0x1","0x1","0x2","0x2","0xffff","0x1f4","0x78859e5b97166c486532b1595a673e9f9073643f1b519c6f18511b9913","0x2","0x389","0x0","0x0","0x1","0x0","0x3e3d6d5ff042148d326c1898713a76759ca273","0xb94f5374fce5edbc8e2a8697c15331677e6ebf1b"],"depth":1,"refund":0,"opName":"RETURNDATACOPY","error":"Out of bounds"},
|
||||
{"output":"","gasUsed":"0x7a1200","test":"00000936-mixed-1","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xd14c10ed22a1cfb642e374be985ac581c39f3969bd59249e0405aca3beb47a47","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":false}
|
||||
{"output":"","gasUsed":"0x7a1200","test":"00000936-mixed-1","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xd14c10ed22a1cfb642e374be985ac581c39f3969bd59249e0405aca3beb47a47","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":false,"error":"INVALID_RETURN_DATA_BUFFER_ACCESS"}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"cli": [
|
||||
"state-test",
|
||||
"stdin",
|
||||
"--trace",
|
||||
"--trace.memory",
|
||||
"--trace.stack",
|
||||
"--trace.returndata",
|
||||
"--notime"
|
||||
],
|
||||
"stdin": {
|
||||
"create-eof": {
|
||||
"env": {
|
||||
"currentCoinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||
"currentDifficulty": "0x20000",
|
||||
"currentRandom": "0x0000000000000000000000000000000000000000000000000000000000020000",
|
||||
"currentGasLimit": "0x26e1f476fe1e22",
|
||||
"currentNumber": "0x2",
|
||||
"currentTimestamp": "0x3e8",
|
||||
"previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"currentBaseFee": "0x10"
|
||||
},
|
||||
"pre": {
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||
"code": "0x",
|
||||
"storage": {},
|
||||
"balance": "0xffffffffff",
|
||||
"nonce": "0x0"
|
||||
}
|
||||
},
|
||||
"transaction": {
|
||||
"gasPrice": "0x10",
|
||||
"nonce": "0x0",
|
||||
"to": null,
|
||||
"data": [
|
||||
"ef00010100040200010009030001001404000000008000035f355f5fa15f5fee00ef00010100040200010001040000000080000000c0de471fe5"
|
||||
],
|
||||
"gasLimit": [
|
||||
"0x7a1200"
|
||||
],
|
||||
"value": [
|
||||
"0xdbbe"
|
||||
],
|
||||
"secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
|
||||
},
|
||||
"out": "0x",
|
||||
"post": {
|
||||
"Prague": [
|
||||
{
|
||||
"hash": "0x1a8642a04dae90535f00f53d3a30284c4db051d508a653db89eb100ba9aecbf3",
|
||||
"logs": "0xf48b954a6a6f4ce6b28e4950b7027413f4bdc8f459df6003b6e8d7a1567c8940",
|
||||
"indexes": {
|
||||
"data": 0,
|
||||
"gas": 0,
|
||||
"value": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"Cancun": [
|
||||
{
|
||||
"hash": "0xaa80d89bc89f58da8de41d3894bd1a241896ff91f7a5964edaefb39e8e3a4a98",
|
||||
"logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"indexes": {
|
||||
"data": 0,
|
||||
"gas": 0,
|
||||
"value": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"stdout": [
|
||||
{"pc":0,"section":0,"op":95,"gas":"0x794068","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":1,"section":0,"op":53,"gas":"0x794066","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"CALLDATALOAD"},
|
||||
{"pc":2,"section":0,"op":95,"gas":"0x794063","gasCost":"0x2","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000"],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":3,"section":0,"op":95,"gas":"0x794061","gasCost":"0x2","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000","0x0"],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":4,"section":0,"op":161,"gas":"0x79405f","gasCost":"0x2ee","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000","0x0","0x0"],"depth":1,"refund":0,"opName":"LOG1"},
|
||||
{"pc":5,"section":0,"op":95,"gas":"0x793d71","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":6,"section":0,"op":95,"gas":"0x793d6f","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":7,"section":0,"op":238,"immediate":"0x00","gas":"0x793d6d","gasCost":"0x0","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"RETURNCONTRACT"},
|
||||
{"output":"","gasUsed":"0xe433","test":"create-eof","fork":"Prague","d":0,"g":0,"v":0,"postHash":"0x1a8642a04dae90535f00f53d3a30284c4db051d508a653db89eb100ba9aecbf3","postLogsHash":"0xf48b954a6a6f4ce6b28e4950b7027413f4bdc8f459df6003b6e8d7a1567c8940","pass":true},
|
||||
{"pc":0,"op":239,"gas":"0x794068","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"INVALID","error":"Bad instruction"},
|
||||
{"output":"","gasUsed":"0x7a1200","test":"create-eof","fork":"Cancun","d":0,"g":0,"v":0,"postHash":"0xaa80d89bc89f58da8de41d3894bd1a241896ff91f7a5964edaefb39e8e3a4a98","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true,"error":"INVALID_OPERATION"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"cli": [
|
||||
"state-test",
|
||||
"stdin",
|
||||
"--trace",
|
||||
"--trace.memory",
|
||||
"--trace.stack",
|
||||
"--trace.returndata",
|
||||
"--notime"
|
||||
],
|
||||
"stdin": {
|
||||
"create-eof": {
|
||||
"env": {
|
||||
"currentCoinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||
"currentDifficulty": "0x20000",
|
||||
"currentRandom": "0x0000000000000000000000000000000000000000000000000000000000020000",
|
||||
"currentGasLimit": "0x26e1f476fe1e22",
|
||||
"currentNumber": "0x2",
|
||||
"currentTimestamp": "0x3e8",
|
||||
"previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"currentBaseFee": "0x10"
|
||||
},
|
||||
"pre": {
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||
"code": "0x",
|
||||
"storage": {},
|
||||
"balance": "0xffffffffff",
|
||||
"nonce": "0x0"
|
||||
}
|
||||
},
|
||||
"transaction": {
|
||||
"gasPrice": "0x10",
|
||||
"nonce": "0x0",
|
||||
"to": null,
|
||||
"data": [
|
||||
"ef00011100040200010009030001001404000000008000035f355f5fa15f5fee00ef00010100040200010001040000000080000000c0de471fe5"
|
||||
],
|
||||
"gasLimit": [
|
||||
"0x7a1200"
|
||||
],
|
||||
"value": [
|
||||
"0xdbbe"
|
||||
],
|
||||
"secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
|
||||
},
|
||||
"out": "0x",
|
||||
"post": {
|
||||
"Prague": [
|
||||
{
|
||||
"hash": "0x1a8642a04dae90535f00f53d3a30284c4db051d508a653db89eb100ba9aecbf3",
|
||||
"logs": "0xf48b954a6a6f4ce6b28e4950b7027413f4bdc8f459df6003b6e8d7a1567c8940",
|
||||
"indexes": {
|
||||
"data": 0,
|
||||
"gas": 0,
|
||||
"value": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"Cancun": [
|
||||
{
|
||||
"hash": "0xaa80d89bc89f58da8de41d3894bd1a241896ff91f7a5964edaefb39e8e3a4a98",
|
||||
"logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"indexes": {
|
||||
"data": 0,
|
||||
"gas": 0,
|
||||
"value": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"stdout": [
|
||||
{"output":"","gasUsed":"0xd198","test":"create-eof","fork":"Prague","d":0,"g":0,"v":0,"postHash":"0x2a9c58298ba5d4ec86ca682b9fcc9ff67c3fc44dbd39f85a2f9b74bfe4e5178e","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":false,"error":"Invalid EOF Layout: Expected kind 1 but read kind 17"},
|
||||
{"pc":0,"op":239,"gas":"0x794068","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"INVALID","error":"Bad instruction"},
|
||||
{"output":"","gasUsed":"0x7a1200","test":"create-eof","fork":"Cancun","d":0,"g":0,"v":0,"postHash":"0xaa80d89bc89f58da8de41d3894bd1a241896ff91f7a5964edaefb39e8e3a4a98","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true,"error":"INVALID_OPERATION"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"cli": [
|
||||
"--notime",
|
||||
"--json",
|
||||
"--create",
|
||||
"--code",
|
||||
"ef00010100040200010009030001001404000000008000035f355f5fa15f5fee00ef00010100040200010001040000000080000000c0de471fe5",
|
||||
"--coinbase",
|
||||
"4444588443C3A91288C5002483449ABA1054192B",
|
||||
"--fork",
|
||||
"pragueeof"
|
||||
],
|
||||
"stdin": "",
|
||||
"stdout": [
|
||||
{"pc":0,"section":0,"op":95,"gas":"0x2540be400","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":1,"section":0,"op":53,"gas":"0x2540be3fe","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"CALLDATALOAD"},
|
||||
{"pc":2,"section":0,"op":95,"gas":"0x2540be3fb","gasCost":"0x2","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000"],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":3,"section":0,"op":95,"gas":"0x2540be3f9","gasCost":"0x2","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000","0x0"],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":4,"section":0,"op":161,"gas":"0x2540be3f7","gasCost":"0x2ee","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000","0x0","0x0"],"depth":1,"refund":0,"opName":"LOG1"},
|
||||
{"pc":5,"section":0,"op":95,"gas":"0x2540be109","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":6,"section":0,"op":95,"gas":"0x2540be107","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH0"},
|
||||
{"pc":7,"section":0,"op":238,"immediate":"0x00","gas":"0x2540be105","gasCost":"0x0","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"RETURNCONTRACT"},
|
||||
{"gasUser":"0x129b","gasTotal":"0x129b","output":"0x"}
|
||||
]
|
||||
}
|
||||
@@ -129,6 +129,21 @@ def generalstateRegressionReferenceTests = tasks.register("generalstateRegressio
|
||||
)
|
||||
}
|
||||
|
||||
def eofReferenceTests = tasks.register("eofReferenceTests") {
|
||||
final referenceTestsPath = "src/reference-test/external-resources/EOFTests"
|
||||
final generatedTestsPath = "$buildDir/generated/sources/reference-test/$name/java"
|
||||
inputs.files fileTree(referenceTestsPath),
|
||||
fileTree(generatedTestsPath)
|
||||
outputs.files generatedTestsPath
|
||||
generateTestFiles(
|
||||
fileTree(referenceTestsPath),
|
||||
file("src/reference-test/templates/EOFReferenceTest.java.template"),
|
||||
"EOFTests",
|
||||
"$generatedTestsPath/org/hyperledger/besu/ethereum/vm/eof",
|
||||
"EOFReferenceTest"
|
||||
)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
referenceTest {
|
||||
java {
|
||||
@@ -140,7 +155,8 @@ sourceSets {
|
||||
eipStateReferenceTests,
|
||||
executionSpecTests,
|
||||
generalstateReferenceTests,
|
||||
generalstateRegressionReferenceTests
|
||||
generalstateRegressionReferenceTests,
|
||||
eofReferenceTests
|
||||
}
|
||||
resources {
|
||||
srcDirs 'src/reference-test/resources',
|
||||
@@ -247,24 +263,20 @@ def generateTestFiles(FileTree jsonPath, File templateFile, String pathstrip, St
|
||||
mkdir(destination)
|
||||
def referenceTestTemplate = templateFile.text
|
||||
|
||||
// This is how many json files to include in each test file
|
||||
def fileSets = jsonPath.getFiles().collate(5)
|
||||
|
||||
fileSets.eachWithIndex { fileSet, idx ->
|
||||
def paths = []
|
||||
fileSet.each { testJsonFile ->
|
||||
def parentFile = testJsonFile.getParentFile()
|
||||
def parentPathFile = parentFile.getPath().substring(parentFile.getPath().indexOf(pathstrip))
|
||||
if (!testJsonFile.getName().toString().startsWith(".") && !excludedPath.contains(parentPathFile)) {
|
||||
def pathFile = testJsonFile.getPath()
|
||||
paths << pathFile.substring(pathFile.indexOf(pathstrip))
|
||||
}
|
||||
def paths = []
|
||||
jsonPath.getFiles().forEach { testJsonFile ->
|
||||
def parentFile = testJsonFile.getParentFile()
|
||||
def parentPathFile = parentFile.getPath().substring(parentFile.getPath().indexOf(pathstrip))
|
||||
if (!testJsonFile.getName().toString().startsWith(".") && !excludedPath.contains(parentPathFile)) {
|
||||
def pathFile = testJsonFile.getPath()
|
||||
paths << pathFile.substring(pathFile.indexOf(pathstrip))
|
||||
}
|
||||
}
|
||||
|
||||
paths.collate(5).eachWithIndex { tests, idx ->
|
||||
def testFile = file(destination + "/" + namePrefix + "_" + idx + ".java")
|
||||
|
||||
|
||||
def allPaths = '"' + paths.join('", "') + '"'
|
||||
def allPaths = '"' + tests.join('",\n "') + '"'
|
||||
|
||||
def testFileContents = referenceTestTemplate
|
||||
.replaceAll("%%TESTS_FILE%%", allPaths)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.ethereum.referencetests;
|
||||
|
||||
import java.util.NavigableMap;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class EOFTestCaseSpec {
|
||||
|
||||
public record TestVector(
|
||||
@JsonProperty("code") String code,
|
||||
@JsonProperty("results") NavigableMap<String, TestResult> results) {}
|
||||
|
||||
public record TestResult(
|
||||
@JsonProperty("exception") String exception, @JsonProperty("result") boolean result) {
|
||||
public static TestResult TEST_RESULT_PASSED = new TestResult(null, true);
|
||||
|
||||
public static TestResult failed(final String exception) {
|
||||
return new TestResult(exception, false);
|
||||
}
|
||||
|
||||
public static TestResult passed() {
|
||||
return TEST_RESULT_PASSED;
|
||||
}
|
||||
}
|
||||
|
||||
NavigableMap<String, TestVector> vector;
|
||||
|
||||
@JsonCreator
|
||||
public EOFTestCaseSpec(@JsonProperty("vectors") final NavigableMap<String, TestVector> vector) {
|
||||
this.vector = vector;
|
||||
}
|
||||
|
||||
public NavigableMap<String, TestVector> getVector() {
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public class ReferenceTestProtocolSchedules {
|
||||
builder.put(
|
||||
"CancunToPragueAtTime15k",
|
||||
createSchedule(genesisStub.clone().cancunTime(0).pragueTime(15000)));
|
||||
builder.put("Prague", createSchedule(genesisStub.clone().pragueTime(0)));
|
||||
builder.put("Prague", createSchedule(genesisStub.clone().pragueEOFTime(0)));
|
||||
builder.put("Future_EIPs", createSchedule(genesisStub.clone().futureEipsTime(0)));
|
||||
builder.put("Experimental_EIPs", createSchedule(genesisStub.clone().experimentalEipsTime(0)));
|
||||
return new ReferenceTestProtocolSchedules(builder.build());
|
||||
|
||||
@@ -111,7 +111,7 @@ public class StateTestVersionedTransaction {
|
||||
this.maxFeePerGas = Optional.ofNullable(maxFeePerGas).map(Wei::fromHexString).orElse(null);
|
||||
this.maxPriorityFeePerGas =
|
||||
Optional.ofNullable(maxPriorityFeePerGas).map(Wei::fromHexString).orElse(null);
|
||||
this.to = to.isEmpty() ? null : Address.fromHexString(to);
|
||||
this.to = (to == null || to.isEmpty()) ? null : Address.fromHexString(to);
|
||||
|
||||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance();
|
||||
this.keys =
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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.ethereum.eof;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EvmSpecVersion;
|
||||
import org.hyperledger.besu.evm.code.CodeFactory;
|
||||
import org.hyperledger.besu.evm.code.CodeInvalid;
|
||||
import org.hyperledger.besu.evm.code.CodeV1;
|
||||
import org.hyperledger.besu.evm.code.CodeV1Validation;
|
||||
import org.hyperledger.besu.evm.code.EOFLayout;
|
||||
import org.hyperledger.besu.testutil.JsonTestParameters;
|
||||
|
||||
public class EOFReferenceTestTools {
|
||||
private static final List<String> EIPS_TO_RUN;
|
||||
|
||||
static {
|
||||
final String eips =
|
||||
System.getProperty("test.ethereum.eof.eips", "Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok");
|
||||
EIPS_TO_RUN = Arrays.asList(eips.split(","));
|
||||
}
|
||||
|
||||
private static final JsonTestParameters<?, ?> params =
|
||||
JsonTestParameters.create(EOFTestCaseSpec.class, EOFTestCaseSpec.TestResult.class)
|
||||
.generator(
|
||||
(testName, fullPath, eofSpec, collector) -> {
|
||||
final Path path = Path.of(fullPath).getParent().getFileName();
|
||||
final String prefix = path + "/" + testName + "-";
|
||||
for (final Map.Entry<String, EOFTestCaseSpec.TestVector> entry :
|
||||
eofSpec.getVector().entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
final Bytes code = Bytes.fromHexString(entry.getValue().code());
|
||||
for (final var result : entry.getValue().results().entrySet()) {
|
||||
final String eip = result.getKey();
|
||||
final boolean runTest = EIPS_TO_RUN.contains(eip);
|
||||
collector.add(
|
||||
prefix + eip + '[' + name + ']',
|
||||
fullPath,
|
||||
eip,
|
||||
code,
|
||||
result.getValue(),
|
||||
runTest);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
static {
|
||||
if (EIPS_TO_RUN.isEmpty()) {
|
||||
params.ignoreAll();
|
||||
}
|
||||
|
||||
// TXCREATE still in tests, but has been removed
|
||||
params.ignore("EOF1_undefined_opcodes_186");
|
||||
}
|
||||
|
||||
private EOFReferenceTestTools() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
//
|
||||
public static Collection<Object[]> generateTestParametersForConfig(final String[] filePath) {
|
||||
return params.generate(filePath);
|
||||
}
|
||||
|
||||
public static void executeTest(
|
||||
final String fork, final Bytes code, final EOFTestCaseSpec.TestResult expected) {
|
||||
EvmSpecVersion evmVersion = EvmSpecVersion.fromName(fork);
|
||||
assertThat(evmVersion).isNotNull();
|
||||
|
||||
// hardwire in the magic byte transaction checks
|
||||
if (evmVersion.getMaxEofVersion() < 1) {
|
||||
assertThat(expected.exception()).isEqualTo("EOF_InvalidCode");
|
||||
} else {
|
||||
EOFLayout layout = EOFLayout.parseEOF(code);
|
||||
|
||||
if (layout.isValid()) {
|
||||
Code parsedCode = CodeFactory.createCode(code, evmVersion.getMaxEofVersion());
|
||||
assertThat(parsedCode.isValid())
|
||||
.withFailMessage(
|
||||
() ->
|
||||
EOFLayout.parseEOF(code).prettyPrint()
|
||||
+ "\nExpected exception :"
|
||||
+ expected.exception()
|
||||
+ " actual exception :"
|
||||
+ (parsedCode.isValid()
|
||||
? null
|
||||
: ((CodeInvalid) parsedCode).getInvalidReason()))
|
||||
.isEqualTo(expected.result());
|
||||
if (parsedCode instanceof CodeV1 codeV1) {
|
||||
var deepValidate = CodeV1Validation.validate(codeV1.getEofLayout());
|
||||
assertThat(deepValidate)
|
||||
.withFailMessage(
|
||||
() ->
|
||||
codeV1.prettyPrint()
|
||||
+ "\nExpected exception :"
|
||||
+ expected.exception()
|
||||
+ " actual exception :"
|
||||
+ (parsedCode.isValid() ? null : deepValidate))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
if (expected.result()) {
|
||||
System.out.println(code);
|
||||
System.out.println(layout.writeContainer(null));
|
||||
assertThat(code)
|
||||
.withFailMessage("Container round trip failed")
|
||||
.isEqualTo(layout.writeContainer(null));
|
||||
}
|
||||
} else {
|
||||
assertThat(layout.isValid())
|
||||
.withFailMessage(
|
||||
() ->
|
||||
"Expected exception - "
|
||||
+ expected.exception()
|
||||
+ " actual exception - "
|
||||
+ (layout.isValid() ? null : layout.invalidReason()))
|
||||
.isEqualTo(expected.result());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,9 +53,9 @@ public class BlockchainReferenceTestTools {
|
||||
final String networks =
|
||||
System.getProperty(
|
||||
"test.ethereum.blockchain.eips",
|
||||
"FrontierToHomesteadAt5,HomesteadToEIP150At5,HomesteadToDaoAt5,EIP158ToByzantiumAt5,"
|
||||
"FrontierToHomesteadAt5,HomesteadToEIP150At5,HomesteadToDaoAt5,EIP158ToByzantiumAt5,CancunToPragueAtTime15k"
|
||||
+ "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix,Istanbul,Berlin,"
|
||||
+ "London,Merge,Paris,Shanghai,Cancun,Prague,Osaka,Bogota,CancunToPragueAtTime15k");
|
||||
+ "London,Merge,Paris,Shanghai,Cancun,Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok");
|
||||
NETWORKS_TO_RUN = Arrays.asList(networks.split(","));
|
||||
}
|
||||
|
||||
@@ -75,21 +75,22 @@ public class BlockchainReferenceTestTools {
|
||||
|
||||
// Consumes a huge amount of memory
|
||||
params.ignore("static_Call1MB1024Calldepth_d1g0v0_\\w+");
|
||||
params.ignore("ShanghaiLove_.*");
|
||||
params.ignore("ShanghaiLove_");
|
||||
|
||||
// Absurd amount of gas, doesn't run in parallel
|
||||
params.ignore("randomStatetest94_\\w+");
|
||||
|
||||
// Don't do time-consuming tests
|
||||
params.ignore("CALLBlake2f_MaxRounds.*");
|
||||
params.ignore("loopMul_*");
|
||||
params.ignore("CALLBlake2f_MaxRounds");
|
||||
params.ignore("loopMul_");
|
||||
|
||||
// Inconclusive fork choice rule, since in merge CL should be choosing forks and setting the
|
||||
// chain head.
|
||||
// Perfectly valid test pre-merge.
|
||||
params.ignore("UncleFromSideChain_(Merge|Paris|Shanghai|Cancun|Prague|Osaka|Bogota)");
|
||||
params.ignore(
|
||||
"UncleFromSideChain_(Merge|Paris|Shanghai|Cancun|Prague|Osaka|Amsterdam|Bogota|Polis|Bangkok)");
|
||||
|
||||
// EOF tests are written against an older version of the spec
|
||||
// EOF tests don't have Prague stuff like deopsits right now
|
||||
params.ignore("/stEOF/");
|
||||
|
||||
// None of the Prague tests have withdrawls and deposits handling
|
||||
|
||||
@@ -66,7 +66,7 @@ public class GeneralStateReferenceTestTools {
|
||||
System.getProperty(
|
||||
"test.ethereum.state.eips",
|
||||
"Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix,Istanbul,Berlin,"
|
||||
+ "London,Merge,Paris,Shanghai,Cancun,Prague,Osaka,Bogota");
|
||||
+ "London,Merge,Paris,Shanghai,Cancun,Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok");
|
||||
EIPS_TO_RUN = Arrays.asList(eips.split(","));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,20 +16,20 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
/** The blockchain test operation testing framework entry point. */
|
||||
public class %%TESTS_NAME%% {
|
||||
|
||||
private static final String[] TEST_CONFIG_FILE_DIR_PATH = new String[] {%%TESTS_FILE%%};
|
||||
private static final String[] TEST_CONFIG_FILE_DIR_PATH =
|
||||
new String[] {
|
||||
%%TESTS_FILE%%
|
||||
};
|
||||
|
||||
public static Stream<Arguments> getTestParametersForConfig() {
|
||||
return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream().map(params ->
|
||||
Arguments.of(params[0], params[1], params[2])
|
||||
);
|
||||
return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream()
|
||||
.map(params -> Arguments.of(params[0], params[1], params[2]));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "Name: {0}")
|
||||
@MethodSource("getTestParametersForConfig")
|
||||
public void execution(
|
||||
final String name,
|
||||
final BlockchainReferenceTestCaseSpec spec,
|
||||
final boolean runTest) {
|
||||
final String name, final BlockchainReferenceTestCaseSpec spec, final boolean runTest) {
|
||||
assumeTrue(runTest, "Test " + name + " was ignored");
|
||||
executeTest(spec);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.hyperledger.besu.ethereum.vm.eof;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.eof.EOFReferenceTestTools.executeTest;
|
||||
import static org.hyperledger.besu.ethereum.eof.EOFReferenceTestTools.generateTestParametersForConfig;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
/** The general state test operation testing framework entry point. */
|
||||
public class %%TESTS_NAME%% {
|
||||
|
||||
private static final String[] TEST_CONFIG_FILE_DIR_PATH =
|
||||
new String[] {
|
||||
%%TESTS_FILE%%
|
||||
};
|
||||
|
||||
public static Stream<Arguments> getTestParametersForConfig() {
|
||||
return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream().map(Arguments::of);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "Name: {0}")
|
||||
@MethodSource("getTestParametersForConfig")
|
||||
public void execution(
|
||||
final String name,
|
||||
final String fork,
|
||||
final Bytes code,
|
||||
final EOFTestCaseSpec.TestResult results,
|
||||
final boolean runTest) {
|
||||
assumeTrue(runTest, "Test " + name + " was ignored");
|
||||
executeTest(fork, code, results);
|
||||
}
|
||||
}
|
||||
@@ -17,20 +17,20 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
/** The general state test operation testing framework entry point. */
|
||||
public class %%TESTS_NAME%% {
|
||||
|
||||
private static final String[] TEST_CONFIG_FILE_DIR_PATH = new String[] {%%TESTS_FILE%%};
|
||||
private static final String[] TEST_CONFIG_FILE_DIR_PATH =
|
||||
new String[] {
|
||||
%%TESTS_FILE%%
|
||||
};
|
||||
|
||||
public static Stream<Arguments> getTestParametersForConfig() {
|
||||
return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream().map(params ->
|
||||
Arguments.of(params[0], params[1], params[2])
|
||||
);
|
||||
return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream()
|
||||
.map(params -> Arguments.of(params[0], params[1], params[2]));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "Name: {0}")
|
||||
@MethodSource("getTestParametersForConfig")
|
||||
public void execution(
|
||||
final String name,
|
||||
final GeneralStateTestCaseEipSpec spec,
|
||||
final boolean runTest) {
|
||||
final String name, final GeneralStateTestCaseEipSpec spec, final boolean runTest) {
|
||||
assumeTrue(runTest, "Test " + name + " was ignored");
|
||||
executeTest(spec);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.hyperledger.besu.evm;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.evm.code.CodeSection;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** Represents EVM code associated with an account. */
|
||||
@@ -30,6 +32,13 @@ public interface Code {
|
||||
*/
|
||||
int getSize();
|
||||
|
||||
/**
|
||||
* Size of the data in bytes. This is for the data only,
|
||||
*
|
||||
* @return size of code in bytes.
|
||||
*/
|
||||
int getDataSize();
|
||||
|
||||
/**
|
||||
* Get the bytes for the entire container, for example what EXTCODECOPY would want. For V0 it is
|
||||
* the same as getCodeBytes, for V1 it is the entire container, not just the data section.
|
||||
@@ -82,4 +91,63 @@ public interface Code {
|
||||
* @return The version of hte ode.
|
||||
*/
|
||||
int getEofVersion();
|
||||
|
||||
/**
|
||||
* Returns the count of subcontainers, or zero if there are none or if the code version does not
|
||||
* support subcontainers.
|
||||
*
|
||||
* @return The subcontainer count or zero if not supported;
|
||||
*/
|
||||
int getSubcontainerCount();
|
||||
|
||||
/**
|
||||
* Returns the subcontainer at the selected index. If the container doesn't exist or is invalid,
|
||||
* an empty result is returned. Legacy code always returns empty.
|
||||
*
|
||||
* @param index the index in the container to return
|
||||
* @param auxData any Auxiliary data to append to the subcontainer code. If fetching an initcode
|
||||
* container, pass null.
|
||||
* @return Either the subcontainer, or empty.
|
||||
*/
|
||||
Optional<Code> getSubContainer(final int index, final Bytes auxData);
|
||||
|
||||
/**
|
||||
* Loads data from the appropriate data section
|
||||
*
|
||||
* @param offset Where within the data section to start copying
|
||||
* @param length how many bytes to copy
|
||||
* @return A slice of the code containing the requested data
|
||||
*/
|
||||
Bytes getData(final int offset, final int length);
|
||||
|
||||
/**
|
||||
* Read a signed 16-bit big-endian integer
|
||||
*
|
||||
* @param startIndex the index to start reading the integer in the code
|
||||
* @return a java int representing the 16-bit signed integer.
|
||||
*/
|
||||
int readBigEndianI16(final int startIndex);
|
||||
|
||||
/**
|
||||
* Read an unsigned 16 bit big-endian integer
|
||||
*
|
||||
* @param startIndex the index to start reading the integer in the code
|
||||
* @return a java int representing the 16-bit unsigned integer.
|
||||
*/
|
||||
int readBigEndianU16(final int startIndex);
|
||||
|
||||
/**
|
||||
* Read an unsigned 8-bit integer
|
||||
*
|
||||
* @param startIndex the index to start reading the integer in the code
|
||||
* @return a java int representing the 8-bit unsigned integer.
|
||||
*/
|
||||
int readU8(final int startIndex);
|
||||
|
||||
/**
|
||||
* A more readable representation of the hex bytes, including whitespace and comments after hashes
|
||||
*
|
||||
* @return The pretty printed code
|
||||
*/
|
||||
String prettyPrint();
|
||||
}
|
||||
|
||||
@@ -365,6 +365,16 @@ public class EVM {
|
||||
* @return the code
|
||||
*/
|
||||
public Code getCodeUncached(final Bytes codeBytes) {
|
||||
return CodeFactory.createCode(codeBytes, evmSpecVersion.getMaxEofVersion(), false);
|
||||
return CodeFactory.createCode(codeBytes, evmSpecVersion.getMaxEofVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets code for creation. Skips code cache and allows for extra data after EOF contracts.
|
||||
*
|
||||
* @param codeBytes the code bytes
|
||||
* @return the code
|
||||
*/
|
||||
public Code getCodeForCreation(final Bytes codeBytes) {
|
||||
return CodeFactory.createCode(codeBytes, evmSpecVersion.getMaxEofVersion(), false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,18 @@ public enum EvmSpecVersion {
|
||||
CANCUN(0, true, "Cancun", "Finalized"),
|
||||
/** Prague evm spec version. */
|
||||
PRAGUE(0, false, "Prague", "In Development"),
|
||||
/** PragueEOF evm spec version. */
|
||||
PRAGUE_EOF(1, false, "PragueEOF", "Prague + EOF. In Development"),
|
||||
/** Osaka evm spec version. */
|
||||
OSAKA(0, false, "Osaka", "Placeholder"),
|
||||
OSAKA(1, false, "Osaka", "Placeholder"),
|
||||
/** Amstedam evm spec version. */
|
||||
AMSTERDAM(1, false, "Amsterdam", "Placeholder"),
|
||||
/** Bogota evm spec version. */
|
||||
BOGOTA(0, false, "Bogota", "Placeholder"),
|
||||
BOGOTA(1, false, "Bogota", "Placeholder"),
|
||||
/** Polis evm spec version. */
|
||||
POLIS(1, false, "Polis", "Placeholder"),
|
||||
/** Bogota evm spec version. */
|
||||
BANGKOK(1, false, "Bangkok", "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 */
|
||||
@@ -146,6 +154,10 @@ public enum EvmSpecVersion {
|
||||
* @return the EVM spec version for that fork, or null if no fork matched.
|
||||
*/
|
||||
public static EvmSpecVersion fromName(final String name) {
|
||||
// TODO remove once PragueEOF settles
|
||||
if ("prague".equalsIgnoreCase(name)) {
|
||||
return EvmSpecVersion.PRAGUE_EOF;
|
||||
}
|
||||
for (var version : EvmSpecVersion.values()) {
|
||||
if (version.name().equalsIgnoreCase(name)) {
|
||||
return version;
|
||||
|
||||
@@ -24,6 +24,7 @@ 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;
|
||||
import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
|
||||
@@ -53,15 +54,25 @@ import org.hyperledger.besu.evm.operation.CodeSizeOperation;
|
||||
import org.hyperledger.besu.evm.operation.CoinbaseOperation;
|
||||
import org.hyperledger.besu.evm.operation.Create2Operation;
|
||||
import org.hyperledger.besu.evm.operation.CreateOperation;
|
||||
import org.hyperledger.besu.evm.operation.DataCopyOperation;
|
||||
import org.hyperledger.besu.evm.operation.DataLoadNOperation;
|
||||
import org.hyperledger.besu.evm.operation.DataLoadOperation;
|
||||
import org.hyperledger.besu.evm.operation.DataSizeOperation;
|
||||
import org.hyperledger.besu.evm.operation.DelegateCallOperation;
|
||||
import org.hyperledger.besu.evm.operation.DifficultyOperation;
|
||||
import org.hyperledger.besu.evm.operation.DivOperation;
|
||||
import org.hyperledger.besu.evm.operation.DupNOperation;
|
||||
import org.hyperledger.besu.evm.operation.DupOperation;
|
||||
import org.hyperledger.besu.evm.operation.EOFCreateOperation;
|
||||
import org.hyperledger.besu.evm.operation.EqOperation;
|
||||
import org.hyperledger.besu.evm.operation.ExchangeOperation;
|
||||
import org.hyperledger.besu.evm.operation.ExpOperation;
|
||||
import org.hyperledger.besu.evm.operation.ExtCallOperation;
|
||||
import org.hyperledger.besu.evm.operation.ExtCodeCopyOperation;
|
||||
import org.hyperledger.besu.evm.operation.ExtCodeHashOperation;
|
||||
import org.hyperledger.besu.evm.operation.ExtCodeSizeOperation;
|
||||
import org.hyperledger.besu.evm.operation.ExtDelegateCallOperation;
|
||||
import org.hyperledger.besu.evm.operation.ExtStaticCallOperation;
|
||||
import org.hyperledger.besu.evm.operation.GasLimitOperation;
|
||||
import org.hyperledger.besu.evm.operation.GasOperation;
|
||||
import org.hyperledger.besu.evm.operation.GasPriceOperation;
|
||||
@@ -69,6 +80,7 @@ import org.hyperledger.besu.evm.operation.GtOperation;
|
||||
import org.hyperledger.besu.evm.operation.InvalidOperation;
|
||||
import org.hyperledger.besu.evm.operation.IsZeroOperation;
|
||||
import org.hyperledger.besu.evm.operation.JumpDestOperation;
|
||||
import org.hyperledger.besu.evm.operation.JumpFOperation;
|
||||
import org.hyperledger.besu.evm.operation.JumpOperation;
|
||||
import org.hyperledger.besu.evm.operation.JumpiOperation;
|
||||
import org.hyperledger.besu.evm.operation.Keccak256Operation;
|
||||
@@ -96,7 +108,9 @@ import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation;
|
||||
import org.hyperledger.besu.evm.operation.RelativeJumpOperation;
|
||||
import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation;
|
||||
import org.hyperledger.besu.evm.operation.RetFOperation;
|
||||
import org.hyperledger.besu.evm.operation.ReturnContractOperation;
|
||||
import org.hyperledger.besu.evm.operation.ReturnDataCopyOperation;
|
||||
import org.hyperledger.besu.evm.operation.ReturnDataLoadOperation;
|
||||
import org.hyperledger.besu.evm.operation.ReturnDataSizeOperation;
|
||||
import org.hyperledger.besu.evm.operation.ReturnOperation;
|
||||
import org.hyperledger.besu.evm.operation.RevertOperation;
|
||||
@@ -115,6 +129,7 @@ import org.hyperledger.besu.evm.operation.SignExtendOperation;
|
||||
import org.hyperledger.besu.evm.operation.StaticCallOperation;
|
||||
import org.hyperledger.besu.evm.operation.StopOperation;
|
||||
import org.hyperledger.besu.evm.operation.SubOperation;
|
||||
import org.hyperledger.besu.evm.operation.SwapNOperation;
|
||||
import org.hyperledger.besu.evm.operation.SwapOperation;
|
||||
import org.hyperledger.besu.evm.operation.TLoadOperation;
|
||||
import org.hyperledger.besu.evm.operation.TStoreOperation;
|
||||
@@ -952,6 +967,107 @@ public class MainnetEVMs {
|
||||
// TODO add EOF operations here once PragueEOF is collapsed into Prague
|
||||
}
|
||||
|
||||
/**
|
||||
* PragueEOF evm.
|
||||
*
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM pragueEOF(final EvmConfiguration evmConfiguration) {
|
||||
return pragueEOF(DEV_NET_CHAIN_ID, evmConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* PragueEOF evm.
|
||||
*
|
||||
* @param chainId the chain id
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM pragueEOF(final BigInteger chainId, final EvmConfiguration evmConfiguration) {
|
||||
return pragueEOF(new PragueEOFGasCalculator(), chainId, evmConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* PragueEOF evm.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainId the chain id
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM pragueEOF(
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainId,
|
||||
final EvmConfiguration evmConfiguration) {
|
||||
return new EVM(
|
||||
pragueEOFOperations(gasCalculator, chainId),
|
||||
gasCalculator,
|
||||
evmConfiguration,
|
||||
EvmSpecVersion.PRAGUE_EOF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation registry for PragueEOF's operations.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainId the chain id
|
||||
* @return the operation registry
|
||||
*/
|
||||
public static OperationRegistry pragueEOFOperations(
|
||||
final GasCalculator gasCalculator, final BigInteger chainId) {
|
||||
OperationRegistry operationRegistry = new OperationRegistry();
|
||||
registerPragueEOFOperations(operationRegistry, gasCalculator, chainId);
|
||||
return operationRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register PragueEOF's operations.
|
||||
*
|
||||
* @param registry the registry
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainID the chain id
|
||||
*/
|
||||
public static void registerPragueEOFOperations(
|
||||
final OperationRegistry registry,
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainID) {
|
||||
registerPragueOperations(registry, gasCalculator, chainID);
|
||||
|
||||
// EIP-663 Unlimited Swap and Dup
|
||||
registry.put(new DupNOperation(gasCalculator));
|
||||
registry.put(new SwapNOperation(gasCalculator));
|
||||
registry.put(new ExchangeOperation(gasCalculator));
|
||||
|
||||
// EIP-4200 relative jump
|
||||
registry.put(new RelativeJumpOperation(gasCalculator));
|
||||
registry.put(new RelativeJumpIfOperation(gasCalculator));
|
||||
registry.put(new RelativeJumpVectorOperation(gasCalculator));
|
||||
|
||||
// EIP-4750 EOF Code Sections
|
||||
registry.put(new CallFOperation(gasCalculator));
|
||||
registry.put(new RetFOperation(gasCalculator));
|
||||
|
||||
// EIP-6209 JUMPF Instruction
|
||||
registry.put(new JumpFOperation(gasCalculator));
|
||||
|
||||
// EIP-7069 Revamped EOF Call
|
||||
registry.put(new ExtCallOperation(gasCalculator));
|
||||
registry.put(new ExtDelegateCallOperation(gasCalculator));
|
||||
registry.put(new ExtStaticCallOperation(gasCalculator));
|
||||
registry.put(new ReturnDataLoadOperation(gasCalculator));
|
||||
|
||||
// EIP-7480 EOF Data Section Access
|
||||
registry.put(new DataLoadOperation(gasCalculator));
|
||||
registry.put(new DataLoadNOperation(gasCalculator));
|
||||
registry.put(new DataSizeOperation(gasCalculator));
|
||||
registry.put(new DataCopyOperation(gasCalculator));
|
||||
|
||||
// EIP-7620 EOF Create and Return Contract operation
|
||||
registry.put(new EOFCreateOperation(gasCalculator));
|
||||
registry.put(new ReturnContractOperation(gasCalculator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Osaka evm.
|
||||
*
|
||||
@@ -1017,7 +1133,75 @@ public class MainnetEVMs {
|
||||
final OperationRegistry registry,
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainID) {
|
||||
registerPragueOperations(registry, gasCalculator, chainID);
|
||||
registerPragueEOFOperations(registry, gasCalculator, chainID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Amsterdam evm.
|
||||
*
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM amsterdam(final EvmConfiguration evmConfiguration) {
|
||||
return amsterdam(DEV_NET_CHAIN_ID, evmConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Amsterdam evm.
|
||||
*
|
||||
* @param chainId the chain id
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM amsterdam(final BigInteger chainId, final EvmConfiguration evmConfiguration) {
|
||||
return amsterdam(new PragueGasCalculator(), chainId, evmConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Amsterdam evm.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainId the chain id
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM amsterdam(
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainId,
|
||||
final EvmConfiguration evmConfiguration) {
|
||||
return new EVM(
|
||||
amsterdamOperations(gasCalculator, chainId),
|
||||
gasCalculator,
|
||||
evmConfiguration,
|
||||
EvmSpecVersion.AMSTERDAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation registry for amsterdam's operations.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainId the chain id
|
||||
* @return the operation registry
|
||||
*/
|
||||
public static OperationRegistry amsterdamOperations(
|
||||
final GasCalculator gasCalculator, final BigInteger chainId) {
|
||||
OperationRegistry operationRegistry = new OperationRegistry();
|
||||
registerAmsterdamOperations(operationRegistry, gasCalculator, chainId);
|
||||
return operationRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register amsterdam operations.
|
||||
*
|
||||
* @param registry the registry
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainID the chain id
|
||||
*/
|
||||
public static void registerAmsterdamOperations(
|
||||
final OperationRegistry registry,
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainID) {
|
||||
registerOsakaOperations(registry, gasCalculator, chainID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1085,7 +1269,143 @@ public class MainnetEVMs {
|
||||
final OperationRegistry registry,
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainID) {
|
||||
registerOsakaOperations(registry, gasCalculator, chainID);
|
||||
registerAmsterdamOperations(registry, gasCalculator, chainID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polis evm.
|
||||
*
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM polis(final EvmConfiguration evmConfiguration) {
|
||||
return polis(DEV_NET_CHAIN_ID, evmConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polis evm.
|
||||
*
|
||||
* @param chainId the chain id
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM polis(final BigInteger chainId, final EvmConfiguration evmConfiguration) {
|
||||
return polis(new PragueGasCalculator(), chainId, evmConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polis evm.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainId the chain id
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM polis(
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainId,
|
||||
final EvmConfiguration evmConfiguration) {
|
||||
return new EVM(
|
||||
polisOperations(gasCalculator, chainId),
|
||||
gasCalculator,
|
||||
evmConfiguration,
|
||||
EvmSpecVersion.POLIS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation registry for Polis's operations.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainId the chain id
|
||||
* @return the operation registry
|
||||
*/
|
||||
public static OperationRegistry polisOperations(
|
||||
final GasCalculator gasCalculator, final BigInteger chainId) {
|
||||
OperationRegistry operationRegistry = new OperationRegistry();
|
||||
registerPolisOperations(operationRegistry, gasCalculator, chainId);
|
||||
return operationRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register polis operations.
|
||||
*
|
||||
* @param registry the registry
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainID the chain id
|
||||
*/
|
||||
public static void registerPolisOperations(
|
||||
final OperationRegistry registry,
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainID) {
|
||||
registerBogotaOperations(registry, gasCalculator, chainID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bangkok evm.
|
||||
*
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM bangkok(final EvmConfiguration evmConfiguration) {
|
||||
return bangkok(DEV_NET_CHAIN_ID, evmConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bangkok evm.
|
||||
*
|
||||
* @param chainId the chain id
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM bangkok(final BigInteger chainId, final EvmConfiguration evmConfiguration) {
|
||||
return bangkok(new PragueGasCalculator(), chainId, evmConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bangkok evm.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainId the chain id
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm
|
||||
*/
|
||||
public static EVM bangkok(
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainId,
|
||||
final EvmConfiguration evmConfiguration) {
|
||||
return new EVM(
|
||||
bangkokOperations(gasCalculator, chainId),
|
||||
gasCalculator,
|
||||
evmConfiguration,
|
||||
EvmSpecVersion.BANGKOK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation registry for bangkok's operations.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainId the chain id
|
||||
* @return the operation registry
|
||||
*/
|
||||
public static OperationRegistry bangkokOperations(
|
||||
final GasCalculator gasCalculator, final BigInteger chainId) {
|
||||
OperationRegistry operationRegistry = new OperationRegistry();
|
||||
registerBangkokOperations(operationRegistry, gasCalculator, chainId);
|
||||
return operationRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register bangkok operations.
|
||||
*
|
||||
* @param registry the registry
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param chainID the chain id
|
||||
*/
|
||||
public static void registerBangkokOperations(
|
||||
final OperationRegistry registry,
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainID) {
|
||||
registerPolisOperations(registry, gasCalculator, chainID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1154,13 +1474,6 @@ public class MainnetEVMs {
|
||||
final GasCalculator gasCalculator,
|
||||
final BigInteger chainID) {
|
||||
registerBogotaOperations(registry, gasCalculator, chainID);
|
||||
|
||||
// "big" EOF
|
||||
registry.put(new RelativeJumpOperation(gasCalculator));
|
||||
registry.put(new RelativeJumpIfOperation(gasCalculator));
|
||||
registry.put(new RelativeJumpVectorOperation(gasCalculator));
|
||||
registry.put(new CallFOperation(gasCalculator));
|
||||
registry.put(new RetFOperation(gasCalculator));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,8 +14,13 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.code;
|
||||
|
||||
import static org.hyperledger.besu.evm.code.EOFLayout.EOFContainerMode.INITCODE;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.errorprone.annotations.InlineMe;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** The Code factory. */
|
||||
@@ -33,24 +38,57 @@ public final class CodeFactory {
|
||||
*
|
||||
* @param bytes the bytes
|
||||
* @param maxEofVersion the max eof version
|
||||
* @param inCreateOperation the in create operation
|
||||
* @return the code
|
||||
*/
|
||||
public static Code createCode(final Bytes bytes, final int maxEofVersion) {
|
||||
return createCode(bytes, maxEofVersion, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Code.
|
||||
*
|
||||
* @param bytes the bytes
|
||||
* @param maxEofVersion the max eof version
|
||||
* @param legacyCreation Allow some corner cases. `EF` and not `EF00` code
|
||||
* @deprecated use the no boolean or two boolean variant
|
||||
* @return the code
|
||||
*/
|
||||
@Deprecated(since = "24.4.1")
|
||||
@InlineMe(
|
||||
replacement = "CodeFactory.createCode(bytes, maxEofVersion, legacyCreation, false)",
|
||||
imports = "org.hyperledger.besu.evm.code.CodeFactory")
|
||||
public static Code createCode(
|
||||
final Bytes bytes, final int maxEofVersion, final boolean legacyCreation) {
|
||||
return createCode(bytes, maxEofVersion, legacyCreation, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Code.
|
||||
*
|
||||
* @param bytes the bytes
|
||||
* @param maxEofVersion the max eof version
|
||||
* @param legacyCreation Allow some corner cases. `EF` and not `EF00` code
|
||||
* @param createTransaction This is in a create transaction, allow dangling data
|
||||
* @return the code
|
||||
*/
|
||||
public static Code createCode(
|
||||
final Bytes bytes, final int maxEofVersion, final boolean inCreateOperation) {
|
||||
final Bytes bytes,
|
||||
final int maxEofVersion,
|
||||
final boolean legacyCreation,
|
||||
final boolean createTransaction) {
|
||||
if (maxEofVersion == 0) {
|
||||
return new CodeV0(bytes);
|
||||
} else if (maxEofVersion == 1) {
|
||||
int codeSize = bytes.size();
|
||||
if (codeSize > 0 && bytes.get(0) == EOF_LEAD_BYTE) {
|
||||
if (codeSize == 1 && !inCreateOperation) {
|
||||
if (codeSize == 1 && !legacyCreation) {
|
||||
return new CodeV0(bytes);
|
||||
}
|
||||
if (codeSize < 3) {
|
||||
return new CodeInvalid(bytes, "EOF Container too short");
|
||||
}
|
||||
if (bytes.get(1) != 0) {
|
||||
if (inCreateOperation) {
|
||||
if (legacyCreation) {
|
||||
// because some 0xef code made it to mainnet, this is only an error at contract create
|
||||
return new CodeInvalid(bytes, "Incorrect second byte");
|
||||
} else {
|
||||
@@ -62,22 +100,11 @@ public final class CodeFactory {
|
||||
return new CodeInvalid(bytes, "Unsupported EOF Version: " + version);
|
||||
}
|
||||
|
||||
final EOFLayout layout = EOFLayout.parseEOF(bytes);
|
||||
if (!layout.isValid()) {
|
||||
return new CodeInvalid(bytes, "Invalid EOF Layout: " + layout.getInvalidReason());
|
||||
final EOFLayout layout = EOFLayout.parseEOF(bytes, !createTransaction);
|
||||
if (createTransaction) {
|
||||
layout.containerMode().set(INITCODE);
|
||||
}
|
||||
|
||||
final String codeValidationError = CodeV1Validation.validateCode(layout);
|
||||
if (codeValidationError != null) {
|
||||
return new CodeInvalid(bytes, "EOF Code Invalid : " + codeValidationError);
|
||||
}
|
||||
|
||||
final String stackValidationError = CodeV1Validation.validateStack(layout);
|
||||
if (stackValidationError != null) {
|
||||
return new CodeInvalid(bytes, "EOF Code Invalid : " + stackValidationError);
|
||||
}
|
||||
|
||||
return new CodeV1(layout);
|
||||
return createCode(layout, createTransaction);
|
||||
} else {
|
||||
return new CodeV0(bytes);
|
||||
}
|
||||
@@ -85,4 +112,18 @@ public final class CodeFactory {
|
||||
return new CodeInvalid(bytes, "Unsupported max code version " + maxEofVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
static Code createCode(final EOFLayout layout, final boolean createTransaction) {
|
||||
if (!layout.isValid()) {
|
||||
return new CodeInvalid(layout.container(), "Invalid EOF Layout: " + layout.invalidReason());
|
||||
}
|
||||
|
||||
final String validationError = CodeV1Validation.validate(layout);
|
||||
if (validationError != null) {
|
||||
return new CodeInvalid(layout.container(), "EOF Code Invalid : " + validationError);
|
||||
}
|
||||
|
||||
return new CodeV1(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ package org.hyperledger.besu.evm.code;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
@@ -59,6 +61,11 @@ public class CodeInvalid implements Code {
|
||||
return codeBytes.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getBytes() {
|
||||
return codeBytes;
|
||||
@@ -91,6 +98,41 @@ public class CodeInvalid implements Code {
|
||||
|
||||
@Override
|
||||
public int getEofVersion() {
|
||||
return -1;
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubcontainerCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Code> getSubContainer(final int index, final Bytes auxData) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getData(final int offset, final int length) {
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readBigEndianI16(final int index) {
|
||||
return Words.readBigEndianI16(index, codeBytes.toArrayUnsafe());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readBigEndianU16(final int index) {
|
||||
return Words.readBigEndianU16(index, codeBytes.toArrayUnsafe());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readU8(final int index) {
|
||||
return codeBytes.toArrayUnsafe()[index] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prettyPrint() {
|
||||
return codeBytes.toHexString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ public final class CodeSection {
|
||||
/** The byte offset from the beginning of the container that the section starts at */
|
||||
final int entryPoint;
|
||||
|
||||
/** Is this a returing code section (i.e. contains RETF or JUMPF into a returning section)? */
|
||||
final boolean returning;
|
||||
|
||||
/**
|
||||
* Instantiates a new Code section.
|
||||
*
|
||||
@@ -53,7 +56,13 @@ public final class CodeSection {
|
||||
final int entryPoint) {
|
||||
this.length = length;
|
||||
this.inputs = inputs;
|
||||
this.outputs = outputs;
|
||||
if (outputs == 0x80) {
|
||||
this.outputs = 0;
|
||||
returning = false;
|
||||
} else {
|
||||
this.outputs = outputs;
|
||||
returning = true;
|
||||
}
|
||||
this.maxStackHeight = maxStackHeight;
|
||||
this.entryPoint = entryPoint;
|
||||
}
|
||||
@@ -85,6 +94,15 @@ public final class CodeSection {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this code seciton have a RETF return anywhere?
|
||||
*
|
||||
* @return returning
|
||||
*/
|
||||
public boolean isReturning() {
|
||||
return returning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets max stack height.
|
||||
*
|
||||
|
||||
@@ -16,8 +16,10 @@ package org.hyperledger.besu.evm.code;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.operation.JumpDestOperation;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
@@ -57,15 +59,14 @@ public class CodeV0 implements Code {
|
||||
* Returns true if the object is equal to this; otherwise false.
|
||||
*
|
||||
* @param other The object to compare this with.
|
||||
* @return True if the object is equal to this; otherwise false.
|
||||
* @return True if the object is equal to this, otherwise false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == null) return false;
|
||||
if (other == this) return true;
|
||||
if (!(other instanceof CodeV0)) return false;
|
||||
if (!(other instanceof CodeV0 that)) return false;
|
||||
|
||||
final CodeV0 that = (CodeV0) other;
|
||||
return this.bytes.equals(that.bytes);
|
||||
}
|
||||
|
||||
@@ -84,6 +85,11 @@ public class CodeV0 implements Code {
|
||||
return bytes.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getBytes() {
|
||||
return bytes;
|
||||
@@ -137,6 +143,21 @@ public class CodeV0 implements Code {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubcontainerCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Code> getSubContainer(final int index, final Bytes auxData) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getData(final int offset, final int length) {
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate jump destination.
|
||||
*
|
||||
@@ -295,4 +316,24 @@ public class CodeV0 implements Code {
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readBigEndianI16(final int index) {
|
||||
return Words.readBigEndianI16(index, bytes.toArrayUnsafe());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readBigEndianU16(final int index) {
|
||||
return Words.readBigEndianU16(index, bytes.toArrayUnsafe());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readU8(final int index) {
|
||||
return bytes.toArrayUnsafe()[index] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prettyPrint() {
|
||||
return bytes.toHexString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,17 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.MutableBytes;
|
||||
|
||||
/** The CodeV1. */
|
||||
public class CodeV1 implements Code {
|
||||
@@ -34,16 +39,16 @@ public class CodeV1 implements Code {
|
||||
/**
|
||||
* Instantiates a new CodeV1.
|
||||
*
|
||||
* @param layout the layout
|
||||
* @param eofLayout the layout
|
||||
*/
|
||||
CodeV1(final EOFLayout layout) {
|
||||
this.eofLayout = layout;
|
||||
this.codeHash = Suppliers.memoize(() -> Hash.hash(eofLayout.getContainer()));
|
||||
CodeV1(final EOFLayout eofLayout) {
|
||||
this.eofLayout = eofLayout;
|
||||
this.codeHash = Suppliers.memoize(() -> Hash.hash(eofLayout.container()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return eofLayout.getContainer().size();
|
||||
return eofLayout.container().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,7 +65,7 @@ public class CodeV1 implements Code {
|
||||
|
||||
@Override
|
||||
public Bytes getBytes() {
|
||||
return eofLayout.getContainer();
|
||||
return eofLayout.container();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,7 +85,35 @@ public class CodeV1 implements Code {
|
||||
|
||||
@Override
|
||||
public int getEofVersion() {
|
||||
return eofLayout.getVersion();
|
||||
return eofLayout.version();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubcontainerCount() {
|
||||
return eofLayout.getSubcontainerCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Code> getSubContainer(final int index, final Bytes auxData) {
|
||||
EOFLayout subcontainerLayout = eofLayout.getSubcontainer(index);
|
||||
if (auxData != null && !auxData.isEmpty()) {
|
||||
Bytes subcontainerWithAuxData = subcontainerLayout.writeContainer(auxData);
|
||||
if (subcontainerWithAuxData == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
subcontainerLayout = EOFLayout.parseEOF(subcontainerWithAuxData);
|
||||
} else {
|
||||
// if no auxdata is added we must validate data is not truncated separately
|
||||
if (subcontainerLayout.dataLength() != subcontainerLayout.data().size()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
Code subContainerCode = CodeFactory.createCode(subcontainerLayout, auxData == null);
|
||||
|
||||
return subContainerCode.isValid() && subContainerCode.getEofVersion() > 0
|
||||
? Optional.of(subContainerCode)
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,4 +128,56 @@ public class CodeV1 implements Code {
|
||||
public int hashCode() {
|
||||
return Objects.hash(codeHash, eofLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getData(final int offset, final int length) {
|
||||
Bytes data = eofLayout.data();
|
||||
int dataLen = data.size();
|
||||
if (offset > dataLen) {
|
||||
return Bytes.EMPTY;
|
||||
} else if ((offset + length) > dataLen) {
|
||||
byte[] result = new byte[length];
|
||||
MutableBytes mbytes = MutableBytes.wrap(result);
|
||||
data.slice(offset).copyTo(mbytes, 0);
|
||||
return Bytes.wrap(result);
|
||||
} else {
|
||||
return data.slice(offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataSize() {
|
||||
return eofLayout.data().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readBigEndianI16(final int index) {
|
||||
return Words.readBigEndianI16(index, eofLayout.container().toArrayUnsafe());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readBigEndianU16(final int index) {
|
||||
return Words.readBigEndianU16(index, eofLayout.container().toArrayUnsafe());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readU8(final int index) {
|
||||
return eofLayout.container().toArrayUnsafe()[index] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prettyPrint() {
|
||||
StringWriter sw = new StringWriter();
|
||||
eofLayout.prettyPrint(new PrintWriter(sw, true), "", "");
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* The EOFLayout object for the code
|
||||
*
|
||||
* @return the EOFLayout object for the parsed code
|
||||
*/
|
||||
public EOFLayout getEofLayout() {
|
||||
return eofLayout;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,45 +14,97 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.code;
|
||||
|
||||
import static org.hyperledger.besu.evm.code.OpcodeInfo.V1_OPCODES;
|
||||
|
||||
import org.hyperledger.besu.evm.operation.ExchangeOperation;
|
||||
import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation;
|
||||
import org.hyperledger.besu.evm.operation.RelativeJumpOperation;
|
||||
import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** The EOF layout. */
|
||||
public class EOFLayout {
|
||||
/**
|
||||
* The EOF layout.
|
||||
*
|
||||
* @param container The literal EOF bytes fo the whole container
|
||||
* @param version The parsed version id. zero if unparseable.
|
||||
* @param codeSections The parsed Code sections. Null if invalid.
|
||||
* @param subContainers The parsed subcontainers. Null if invalid.
|
||||
* @param dataLength The length of the data as reported by the container. For subcontainers this may
|
||||
* be larger than the data in the data field. Zero if invalid.
|
||||
* @param data The data hard coded in the container. Empty if invalid.
|
||||
* @param invalidReason If the raw container is invalid, the reason it is invalid. Null if valid.
|
||||
* @param containerMode The mode of the container (runtime or initcode, if known)
|
||||
*/
|
||||
public record EOFLayout(
|
||||
Bytes container,
|
||||
int version,
|
||||
CodeSection[] codeSections,
|
||||
EOFLayout[] subContainers,
|
||||
int dataLength,
|
||||
Bytes data,
|
||||
String invalidReason,
|
||||
AtomicReference<EOFContainerMode> containerMode) {
|
||||
|
||||
/** The Section Terminator. */
|
||||
enum EOFContainerMode {
|
||||
UNKNOWN,
|
||||
INITCODE,
|
||||
RUNTIME
|
||||
}
|
||||
|
||||
/** The EOF prefix byte as a (signed) java byte. */
|
||||
public static final byte EOF_PREFIX_BYTE = (byte) 0xEF;
|
||||
|
||||
/** header terminator */
|
||||
static final int SECTION_TERMINATOR = 0x00;
|
||||
|
||||
/** The Section types. */
|
||||
/** type data (stack heights, inputs/outputs) */
|
||||
static final int SECTION_TYPES = 0x01;
|
||||
|
||||
/** The Section code. */
|
||||
/** code */
|
||||
static final int SECTION_CODE = 0x02;
|
||||
|
||||
/** The Section data. */
|
||||
static final int SECTION_DATA = 0x03;
|
||||
/** sub-EOF subContainers for create */
|
||||
static final int SECTION_CONTAINER = 0x03;
|
||||
|
||||
/** data */
|
||||
static final int SECTION_DATA = 0x04;
|
||||
|
||||
/** The Max supported section. */
|
||||
static final int MAX_SUPPORTED_VERSION = 1;
|
||||
|
||||
private final Bytes container;
|
||||
private final int version;
|
||||
private final CodeSection[] codeSections;
|
||||
private final String invalidReason;
|
||||
|
||||
private EOFLayout(final Bytes container, final int version, final CodeSection[] codeSections) {
|
||||
this.container = container;
|
||||
this.version = version;
|
||||
this.codeSections = codeSections;
|
||||
this.invalidReason = null;
|
||||
private EOFLayout(
|
||||
final Bytes container,
|
||||
final int version,
|
||||
final CodeSection[] codeSections,
|
||||
final EOFLayout[] containers,
|
||||
final int dataSize,
|
||||
final Bytes data) {
|
||||
this(
|
||||
container,
|
||||
version,
|
||||
codeSections,
|
||||
containers,
|
||||
dataSize,
|
||||
data,
|
||||
null,
|
||||
new AtomicReference<>(null));
|
||||
}
|
||||
|
||||
private EOFLayout(final Bytes container, final int version, final String invalidReason) {
|
||||
this.container = container;
|
||||
this.version = version;
|
||||
this.codeSections = null;
|
||||
this.invalidReason = invalidReason;
|
||||
this(
|
||||
container, version, null, null, 0, Bytes.EMPTY, invalidReason, new AtomicReference<>(null));
|
||||
}
|
||||
|
||||
private static EOFLayout invalidLayout(
|
||||
@@ -71,6 +123,13 @@ public class EOFLayout {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int peekKind(final ByteArrayInputStream inputStream) {
|
||||
inputStream.mark(1);
|
||||
int kind = inputStream.read();
|
||||
inputStream.reset();
|
||||
return kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse EOF.
|
||||
*
|
||||
@@ -78,6 +137,18 @@ public class EOFLayout {
|
||||
* @return the eof layout
|
||||
*/
|
||||
public static EOFLayout parseEOF(final Bytes container) {
|
||||
return parseEOF(container, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse EOF.
|
||||
*
|
||||
* @param container the container
|
||||
* @param strictSize Require the container to fill all bytes, a validation error will result if
|
||||
* strict and excess data is in the container
|
||||
* @return the eof layout
|
||||
*/
|
||||
public static EOFLayout parseEOF(final Bytes container, final boolean strictSize) {
|
||||
final ByteArrayInputStream inputStream = new ByteArrayInputStream(container.toArrayUnsafe());
|
||||
|
||||
if (inputStream.available() < 3) {
|
||||
@@ -100,7 +171,7 @@ public class EOFLayout {
|
||||
return invalidLayout(container, version, error);
|
||||
}
|
||||
int typesLength = readUnsignedShort(inputStream);
|
||||
if (typesLength <= 0) {
|
||||
if (typesLength <= 0 || typesLength % 4 != 0) {
|
||||
return invalidLayout(container, version, "Invalid Types section size");
|
||||
}
|
||||
|
||||
@@ -136,6 +207,37 @@ public class EOFLayout {
|
||||
codeSectionSizes[i] = size;
|
||||
}
|
||||
|
||||
int containerSectionCount;
|
||||
int[] containerSectionSizes;
|
||||
if (peekKind(inputStream) == SECTION_CONTAINER) {
|
||||
error = readKind(inputStream, SECTION_CONTAINER);
|
||||
if (error != null) {
|
||||
return invalidLayout(container, version, error);
|
||||
}
|
||||
containerSectionCount = readUnsignedShort(inputStream);
|
||||
if (containerSectionCount <= 0) {
|
||||
return invalidLayout(container, version, "Invalid container section count");
|
||||
}
|
||||
if (containerSectionCount > 256) {
|
||||
return invalidLayout(
|
||||
container,
|
||||
version,
|
||||
"Too many container sections - 0x" + Integer.toHexString(containerSectionCount));
|
||||
}
|
||||
containerSectionSizes = new int[containerSectionCount];
|
||||
for (int i = 0; i < containerSectionCount; i++) {
|
||||
int size = readUnsignedShort(inputStream);
|
||||
if (size <= 0) {
|
||||
return invalidLayout(
|
||||
container, version, "Invalid container section size for section " + i);
|
||||
}
|
||||
containerSectionSizes[i] = size;
|
||||
}
|
||||
} else {
|
||||
containerSectionCount = 0;
|
||||
containerSectionSizes = new int[0];
|
||||
}
|
||||
|
||||
error = readKind(inputStream, SECTION_DATA);
|
||||
if (error != null) {
|
||||
return invalidLayout(container, version, error);
|
||||
@@ -172,6 +274,12 @@ public class EOFLayout {
|
||||
+ 3 // data section header
|
||||
+ 1 // padding
|
||||
+ (codeSectionCount * 4); // type data
|
||||
if (containerSectionCount > 0) {
|
||||
pos +=
|
||||
3 // subcontainer header
|
||||
+ (containerSectionCount * 2); // subcontainer sizes
|
||||
}
|
||||
|
||||
for (int i = 0; i < codeSectionCount; i++) {
|
||||
int codeSectionSize = codeSectionSizes[i];
|
||||
if (inputStream.skip(codeSectionSize) != codeSectionSize) {
|
||||
@@ -197,17 +305,52 @@ public class EOFLayout {
|
||||
}
|
||||
codeSections[i] =
|
||||
new CodeSection(codeSectionSize, typeData[i][0], typeData[i][1], typeData[i][2], pos);
|
||||
if (i == 0 && typeData[0][1] != 0x80) {
|
||||
return invalidLayout(
|
||||
container,
|
||||
version,
|
||||
"Code section at zero expected non-returning flag, but had return stack of "
|
||||
+ typeData[0][1]);
|
||||
}
|
||||
pos += codeSectionSize;
|
||||
}
|
||||
|
||||
if (inputStream.skip(dataSize) != dataSize) {
|
||||
return invalidLayout(container, version, "Incomplete data section");
|
||||
}
|
||||
if (inputStream.read() != -1) {
|
||||
return invalidLayout(container, version, "Dangling data after end of all sections");
|
||||
EOFLayout[] subContainers = new EOFLayout[containerSectionCount];
|
||||
for (int i = 0; i < containerSectionCount; i++) {
|
||||
int subcontianerSize = containerSectionSizes[i];
|
||||
if (subcontianerSize != inputStream.skip(subcontianerSize)) {
|
||||
return invalidLayout(container, version, "incomplete subcontainer");
|
||||
}
|
||||
Bytes subcontainer = container.slice(pos, subcontianerSize);
|
||||
pos += subcontianerSize;
|
||||
EOFLayout subLayout = EOFLayout.parseEOF(subcontainer);
|
||||
if (!subLayout.isValid()) {
|
||||
String invalidSubReason = subLayout.invalidReason;
|
||||
return invalidLayout(
|
||||
container,
|
||||
version,
|
||||
invalidSubReason.contains("invalid subcontainer")
|
||||
? invalidSubReason
|
||||
: "invalid subcontainer - " + invalidSubReason);
|
||||
}
|
||||
subContainers[i] = subLayout;
|
||||
}
|
||||
|
||||
return new EOFLayout(container, version, codeSections);
|
||||
long loadedDataCount = inputStream.skip(dataSize);
|
||||
Bytes data = container.slice(pos, (int) loadedDataCount);
|
||||
|
||||
Bytes completeContainer;
|
||||
if (inputStream.read() != -1) {
|
||||
if (strictSize) {
|
||||
return invalidLayout(container, version, "Dangling data after end of all sections");
|
||||
} else {
|
||||
completeContainer = container.slice(0, pos + dataSize);
|
||||
}
|
||||
} else {
|
||||
completeContainer = container;
|
||||
}
|
||||
|
||||
return new EOFLayout(completeContainer, version, codeSections, subContainers, dataSize, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,24 +367,6 @@ public class EOFLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets container.
|
||||
*
|
||||
* @return the container
|
||||
*/
|
||||
public Bytes getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets version.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get code section count.
|
||||
*
|
||||
@@ -262,12 +387,22 @@ public class EOFLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets invalid reason.
|
||||
* Get sub container section count.
|
||||
*
|
||||
* @return the invalid reason
|
||||
* @return the sub container count
|
||||
*/
|
||||
public String getInvalidReason() {
|
||||
return invalidReason;
|
||||
public int getSubcontainerCount() {
|
||||
return subContainers == null ? 0 : subContainers.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get code sections.
|
||||
*
|
||||
* @param i the index
|
||||
* @return the Code section
|
||||
*/
|
||||
public EOFLayout getSubcontainer(final int i) {
|
||||
return subContainers[i];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,4 +413,313 @@ public class EOFLayout {
|
||||
public boolean isValid() {
|
||||
return invalidReason == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof EOFLayout eofLayout)) return false;
|
||||
return version == eofLayout.version
|
||||
&& container.equals(eofLayout.container)
|
||||
&& Arrays.equals(codeSections, eofLayout.codeSections)
|
||||
&& Arrays.equals(subContainers, eofLayout.subContainers)
|
||||
&& Objects.equals(invalidReason, eofLayout.invalidReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(container, version, invalidReason);
|
||||
result = 31 * result + Arrays.hashCode(codeSections);
|
||||
result = 31 * result + Arrays.hashCode(subContainers);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EOFLayout{"
|
||||
+ "container="
|
||||
+ container
|
||||
+ ", version="
|
||||
+ version
|
||||
+ ", codeSections="
|
||||
+ (codeSections == null ? "null" : Arrays.asList(codeSections).toString())
|
||||
+ ", containers="
|
||||
+ (subContainers == null ? "null" : Arrays.asList(subContainers).toString())
|
||||
+ ", invalidReason='"
|
||||
+ invalidReason
|
||||
+ '\''
|
||||
+ '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-writes the container with optional auxiliary data.
|
||||
*
|
||||
* @param auxData the auxiliary data
|
||||
* @return Null if there was an error (validation or otherwise) , or the bytes of the re-written
|
||||
* container.
|
||||
*/
|
||||
@Nullable
|
||||
public Bytes writeContainer(@Nullable final Bytes auxData) {
|
||||
// do not write invalid containers
|
||||
if (invalidReason != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream baos =
|
||||
new ByteArrayOutputStream(container.size() + dataLength - data.size());
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
|
||||
// EOF header
|
||||
out.writeByte(EOF_PREFIX_BYTE);
|
||||
out.writeByte(0);
|
||||
out.writeByte(version);
|
||||
|
||||
// Types header
|
||||
out.writeByte(SECTION_TYPES);
|
||||
out.writeShort(codeSections.length * 4);
|
||||
|
||||
// Code header
|
||||
out.writeByte(SECTION_CODE);
|
||||
out.writeShort(codeSections.length);
|
||||
for (CodeSection cs : codeSections) {
|
||||
out.writeShort(cs.length);
|
||||
}
|
||||
|
||||
// Subcontainers header
|
||||
if (subContainers != null && subContainers.length > 0) {
|
||||
out.writeByte(SECTION_CONTAINER);
|
||||
out.writeShort(subContainers.length);
|
||||
for (EOFLayout container : subContainers) {
|
||||
out.writeShort(container.container.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Data header
|
||||
out.writeByte(SECTION_DATA);
|
||||
if (auxData == null) {
|
||||
out.writeShort(dataLength);
|
||||
} else {
|
||||
int newSize = data.size() + auxData.size();
|
||||
if (newSize < dataLength) {
|
||||
// aux data must cover claimed data lengths.
|
||||
return null;
|
||||
}
|
||||
out.writeShort(newSize);
|
||||
}
|
||||
|
||||
// header end
|
||||
out.writeByte(0);
|
||||
|
||||
// Types information
|
||||
for (CodeSection cs : codeSections) {
|
||||
out.writeByte(cs.inputs);
|
||||
if (cs.returning) {
|
||||
out.writeByte(cs.outputs);
|
||||
} else {
|
||||
out.writeByte(0x80);
|
||||
}
|
||||
out.writeShort(cs.maxStackHeight);
|
||||
}
|
||||
|
||||
// Code sections
|
||||
for (CodeSection cs : codeSections) {
|
||||
out.write(container.slice(cs.entryPoint, cs.length).toArray());
|
||||
}
|
||||
|
||||
// Subcontainers
|
||||
if (subContainers != null) {
|
||||
for (EOFLayout container : subContainers) {
|
||||
out.write(container.container.toArrayUnsafe());
|
||||
}
|
||||
}
|
||||
|
||||
// data
|
||||
out.write(data.toArrayUnsafe());
|
||||
if (auxData != null) {
|
||||
out.write(auxData.toArrayUnsafe());
|
||||
}
|
||||
|
||||
return Bytes.wrap(baos.toByteArray());
|
||||
} catch (IOException ioe) {
|
||||
// ByteArrayOutputStream should never throw, so something has gone very wrong. Wrap as
|
||||
// runtime
|
||||
// and re-throw.
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A more readable representation of the hex bytes, including whitespace and comments after hashes
|
||||
*
|
||||
* @return The pretty printed code
|
||||
*/
|
||||
public String prettyPrint() {
|
||||
StringWriter sw = new StringWriter();
|
||||
prettyPrint(new PrintWriter(sw, true), "", "");
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A more readable representation of the hex bytes, including whitespace and comments after hashes
|
||||
*
|
||||
* @param out the print writer to pretty print to
|
||||
*/
|
||||
public void prettyPrint(final PrintWriter out) {
|
||||
out.println("0x # EOF");
|
||||
prettyPrint(out, "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* A more readable representation of the hex bytes, including whitespace and comments after hashes
|
||||
*
|
||||
* @param out the print writer to pretty print to
|
||||
* @param prefix The prefix to prepend to all output lines (useful for nested subconntainers)
|
||||
* @param subcontainerPrefix The prefix to add to subcontainer names.
|
||||
*/
|
||||
public void prettyPrint(
|
||||
final PrintWriter out, final String prefix, final String subcontainerPrefix) {
|
||||
|
||||
if (!isValid()) {
|
||||
out.print(prefix);
|
||||
out.println("# Invalid EOF");
|
||||
out.print(prefix);
|
||||
out.println("# " + invalidReason);
|
||||
out.println(container);
|
||||
}
|
||||
|
||||
out.print(prefix);
|
||||
out.printf("ef00%02x # Magic and Version ( %1$d )%n", version);
|
||||
out.print(prefix);
|
||||
out.printf("01%04x # Types length ( %1$d )%n", codeSections.length * 4);
|
||||
out.print(prefix);
|
||||
out.printf("02%04x # Total code sections ( %1$d )%n", codeSections.length);
|
||||
for (int i = 0; i < codeSections.length; i++) {
|
||||
out.print(prefix);
|
||||
out.printf(" %04x # Code section %d , %1$d bytes%n", getCodeSection(i).getLength(), i);
|
||||
}
|
||||
if (subContainers.length > 0) {
|
||||
out.print(prefix);
|
||||
out.printf("03%04x # Total subcontainers ( %1$d )%n", subContainers.length);
|
||||
for (int i = 0; i < subContainers.length; i++) {
|
||||
out.print(prefix);
|
||||
out.printf(" %04x # Sub container %d, %1$d byte%n", subContainers[i].container.size(), i);
|
||||
}
|
||||
}
|
||||
out.print(prefix);
|
||||
out.printf("04%04x # Data section length( %1$d )", dataLength);
|
||||
if (dataLength != data.size()) {
|
||||
out.printf(" (actual size %d)", data.size());
|
||||
}
|
||||
out.print(prefix);
|
||||
out.printf("%n");
|
||||
out.print(prefix);
|
||||
out.printf(" 00 # Terminator (end of header)%n");
|
||||
for (int i = 0; i < codeSections.length; i++) {
|
||||
CodeSection cs = getCodeSection(i);
|
||||
out.print(prefix);
|
||||
out.printf(" # Code section %d types%n", i);
|
||||
out.print(prefix);
|
||||
out.printf(" %02x # %1$d inputs %n", cs.getInputs());
|
||||
out.print(prefix);
|
||||
out.printf(
|
||||
" %02x # %d outputs %s%n",
|
||||
cs.isReturning() ? cs.getOutputs() : 0x80,
|
||||
cs.getOutputs(),
|
||||
cs.isReturning() ? "" : " (Non-returning function)");
|
||||
out.print(prefix);
|
||||
out.printf(" %04x # max stack: %1$d%n", cs.getMaxStackHeight());
|
||||
}
|
||||
for (int i = 0; i < codeSections.length; i++) {
|
||||
CodeSection cs = getCodeSection(i);
|
||||
out.print(prefix);
|
||||
out.printf(
|
||||
" # Code section %d - in=%d out=%s height=%d%n",
|
||||
i, cs.inputs, cs.isReturning() ? cs.outputs : "non-returning", cs.maxStackHeight);
|
||||
byte[] byteCode = container.slice(cs.getEntryPoint(), cs.getLength()).toArray();
|
||||
int pc = 0;
|
||||
while (pc < byteCode.length) {
|
||||
out.print(prefix);
|
||||
OpcodeInfo ci = V1_OPCODES[byteCode[pc] & 0xff];
|
||||
|
||||
if (ci.opcode() == RelativeJumpVectorOperation.OPCODE) {
|
||||
int tableSize = byteCode[pc + 1] & 0xff;
|
||||
out.printf("%02x%02x", byteCode[pc], byteCode[pc + 1]);
|
||||
for (int j = 0; j <= tableSize; j++) {
|
||||
out.printf("%02x%02x", byteCode[pc + j * 2 + 2], byteCode[pc + j * 2 + 3]);
|
||||
}
|
||||
out.printf(" # [%d] %s(", pc, ci.name());
|
||||
for (int j = 0; j <= tableSize; j++) {
|
||||
if (j != 0) {
|
||||
out.print(',');
|
||||
}
|
||||
int b0 = byteCode[pc + j * 2 + 2]; // we want the sign extension, so no `& 0xff`
|
||||
int b1 = byteCode[pc + j * 2 + 3] & 0xff;
|
||||
out.print(b0 << 8 | b1);
|
||||
}
|
||||
pc += tableSize * 2 + 4;
|
||||
out.print(")\n");
|
||||
} else if (ci.opcode() == RelativeJumpOperation.OPCODE
|
||||
|| ci.opcode() == RelativeJumpIfOperation.OPCODE) {
|
||||
int b0 = byteCode[pc + 1] & 0xff;
|
||||
int b1 = byteCode[pc + 2] & 0xff;
|
||||
short delta = (short) (b0 << 8 | b1);
|
||||
out.printf("%02x%02x%02x # [%d] %s(%d)", byteCode[pc], b0, b1, pc, ci.name(), delta);
|
||||
pc += 3;
|
||||
out.printf("%n");
|
||||
} else if (ci.opcode() == ExchangeOperation.OPCODE) {
|
||||
int imm = byteCode[pc + 1] & 0xff;
|
||||
out.printf(
|
||||
" %02x%02x # [%d] %s(%d, %d)",
|
||||
byteCode[pc], imm, pc, ci.name(), imm >> 4, imm & 0x0F);
|
||||
pc += 2;
|
||||
out.printf("%n");
|
||||
} else {
|
||||
int advance = ci.pcAdvance();
|
||||
if (advance == 1) {
|
||||
out.print(" ");
|
||||
} else if (advance == 2) {
|
||||
out.print(" ");
|
||||
}
|
||||
out.printf("%02x", byteCode[pc]);
|
||||
for (int j = 1; j < advance; j++) {
|
||||
out.printf("%02x", byteCode[pc + j]);
|
||||
}
|
||||
out.printf(" # [%d] %s", pc, ci.name());
|
||||
if (advance == 2) {
|
||||
out.printf("(%d)", byteCode[pc + 1] & 0xff);
|
||||
} else if (advance > 2) {
|
||||
out.print("(0x");
|
||||
for (int j = 1; j < advance; j++) {
|
||||
out.printf("%02x", byteCode[pc + j]);
|
||||
}
|
||||
out.print(")");
|
||||
}
|
||||
out.printf("%n");
|
||||
pc += advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < subContainers.length; i++) {
|
||||
var subContainer = subContainers[i];
|
||||
out.print(prefix);
|
||||
out.printf(" # Subcontainer %s%d starts here%n", subcontainerPrefix, i);
|
||||
|
||||
subContainer.prettyPrint(out, prefix + " ", subcontainerPrefix + i + ".");
|
||||
out.print(prefix);
|
||||
out.printf(" # Subcontainer %s%d ends%n", subcontainerPrefix, i);
|
||||
}
|
||||
|
||||
out.print(prefix);
|
||||
if (data.isEmpty()) {
|
||||
out.print(" # Data section (empty)\n");
|
||||
} else {
|
||||
out.printf(" # Data section length ( %1$d )", dataLength);
|
||||
if (dataLength != data.size()) {
|
||||
out.printf(" actual length ( %d )", data.size());
|
||||
}
|
||||
out.printf("%n%s %s%n", prefix, data.toUnprefixedHexString());
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
336
evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java
Normal file
336
evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* 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.code;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* Information about opcodes. Currently merges Legacy and EOFv1
|
||||
*
|
||||
* @param name formal name of the opcode, such as STOP
|
||||
* @param opcode the number of the opcode
|
||||
* @param valid Is this a valid opcode (from an EOFV1 perspective)
|
||||
* @param terminal Is this opcode terminal? (i.e. can it end a code section)
|
||||
* @param inputs How many stack inputs are required/consumed?
|
||||
* @param outputs How many stack items will be output?
|
||||
* @param stackDelta What is the net difference in stack height from this operation
|
||||
* @param pcAdvance How far should the PC advance (0 for terminal only, 1 for most, 2+ for opcodes
|
||||
* with immediates)
|
||||
*/
|
||||
public record OpcodeInfo(
|
||||
String name,
|
||||
int opcode,
|
||||
boolean valid,
|
||||
boolean terminal,
|
||||
int inputs,
|
||||
int outputs,
|
||||
int stackDelta,
|
||||
int pcAdvance) {
|
||||
static OpcodeInfo unallocatedOpcode(final int opcode) {
|
||||
return new OpcodeInfo("-", opcode, false, false, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
static OpcodeInfo invalidOpcode(final String name, final int opcode) {
|
||||
return new OpcodeInfo(name, opcode, false, false, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
static OpcodeInfo terminalOpcode(
|
||||
final String name,
|
||||
final int opcode,
|
||||
final int inputs,
|
||||
final int outputs,
|
||||
final int pcAdvance) {
|
||||
return new OpcodeInfo(name, opcode, true, true, inputs, outputs, outputs - inputs, pcAdvance);
|
||||
}
|
||||
|
||||
static OpcodeInfo validOpcode(
|
||||
final String name,
|
||||
final int opcode,
|
||||
final int inputs,
|
||||
final int outputs,
|
||||
final int pcAdvance) {
|
||||
return new OpcodeInfo(name, opcode, true, false, inputs, outputs, outputs - inputs, pcAdvance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the opcode info for a specific opcode
|
||||
*
|
||||
* @param i opcode
|
||||
* @return the OpcodeInfo object describing that opcode
|
||||
*/
|
||||
public static OpcodeInfo getOpcode(final int i) {
|
||||
Preconditions.checkArgument(i >= 0 && i <= 255);
|
||||
return V1_OPCODES[i];
|
||||
}
|
||||
|
||||
static final OpcodeInfo[] V1_OPCODES = {
|
||||
OpcodeInfo.terminalOpcode("STOP", 0x00, 0, 0, 1),
|
||||
OpcodeInfo.validOpcode("ADD", 0x01, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("MUL", 0x02, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SUB", 0x03, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("DIV", 0x04, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SDIV", 0x05, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("MOD", 0x06, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SMOD", 0x07, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("ADDMOD", 0x08, 3, 1, 1),
|
||||
OpcodeInfo.validOpcode("MULMOD", 0x09, 3, 1, 1),
|
||||
OpcodeInfo.validOpcode("EXP", 0x0a, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SIGNEXTEND", 0x0b, 2, 1, 1),
|
||||
OpcodeInfo.unallocatedOpcode(0x0c),
|
||||
OpcodeInfo.unallocatedOpcode(0x0d),
|
||||
OpcodeInfo.unallocatedOpcode(0x0e),
|
||||
OpcodeInfo.unallocatedOpcode(0x0f),
|
||||
OpcodeInfo.validOpcode("LT", 0x10, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("GT", 0x11, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SLT", 0x12, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SGT", 0x13, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("EQ", 0x14, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("ISZERO", 0x15, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("AND", 0x16, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("OR", 0x17, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("XOR", 0x18, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("NOT", 0x19, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("BYTE", 0x1a, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SHL", 0x1b, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SHR", 0x1c, 2, 1, 1),
|
||||
OpcodeInfo.validOpcode("SAR", 0x1d, 2, 1, 1),
|
||||
OpcodeInfo.unallocatedOpcode(0x1e),
|
||||
OpcodeInfo.unallocatedOpcode(0x1f),
|
||||
OpcodeInfo.validOpcode("SHA3", 0x20, 2, 1, 1),
|
||||
OpcodeInfo.unallocatedOpcode(0x21),
|
||||
OpcodeInfo.unallocatedOpcode(0x22),
|
||||
OpcodeInfo.unallocatedOpcode(0x23),
|
||||
OpcodeInfo.unallocatedOpcode(0x24),
|
||||
OpcodeInfo.unallocatedOpcode(0x25),
|
||||
OpcodeInfo.unallocatedOpcode(0x26),
|
||||
OpcodeInfo.unallocatedOpcode(0x27),
|
||||
OpcodeInfo.unallocatedOpcode(0x28),
|
||||
OpcodeInfo.unallocatedOpcode(0x29),
|
||||
OpcodeInfo.unallocatedOpcode(0x2a),
|
||||
OpcodeInfo.unallocatedOpcode(0x2b),
|
||||
OpcodeInfo.unallocatedOpcode(0x2c),
|
||||
OpcodeInfo.unallocatedOpcode(0x2d),
|
||||
OpcodeInfo.unallocatedOpcode(0x2e),
|
||||
OpcodeInfo.unallocatedOpcode(0x2f),
|
||||
OpcodeInfo.validOpcode("ADDRESS", 0x30, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("BALANCE", 0x31, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("ORIGIN", 0x32, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("CALLER", 0x33, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("CALLVALUE", 0x34, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("CALLDATALOAD", 0x35, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("CALLDATASIZE", 0x36, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("CALLDATACOPY", 0x37, 3, 0, 1),
|
||||
OpcodeInfo.invalidOpcode("CODESIZE", 0x38),
|
||||
OpcodeInfo.invalidOpcode("CODECOPY", 0x39),
|
||||
OpcodeInfo.validOpcode("GASPRICE", 0x3a, 0, 1, 1),
|
||||
OpcodeInfo.invalidOpcode("EXTCODESIZE", 0x3b),
|
||||
OpcodeInfo.invalidOpcode("EXTCODECOPY", 0x3c),
|
||||
OpcodeInfo.validOpcode("RETURNDATASIZE", 0x3d, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("RETURNDATACOPY", 0x3e, 3, 0, 1),
|
||||
OpcodeInfo.invalidOpcode("EXTCODEHASH", 0x3f),
|
||||
OpcodeInfo.validOpcode("BLOCKHASH", 0x40, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("COINBASE", 0x41, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("TIMESTAMP", 0x42, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("NUMBER", 0x43, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("PREVRANDAO", 0x44, 0, 1, 1), // was DIFFICULTY
|
||||
OpcodeInfo.validOpcode("GASLIMIT", 0x45, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("CHAINID", 0x46, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("SELFBALANCE", 0x47, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("BASEFEE", 0x48, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("BLOBAHASH", 0x49, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("BLOBBASEFEE", 0x4a, 0, 1, 1),
|
||||
OpcodeInfo.unallocatedOpcode(0x4b),
|
||||
OpcodeInfo.unallocatedOpcode(0x4c),
|
||||
OpcodeInfo.unallocatedOpcode(0x4d),
|
||||
OpcodeInfo.unallocatedOpcode(0x4e),
|
||||
OpcodeInfo.unallocatedOpcode(0x4f),
|
||||
OpcodeInfo.validOpcode("POP", 0x50, 1, 0, 1),
|
||||
OpcodeInfo.validOpcode("MLOAD", 0x51, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("MSTORE", 0x52, 2, 0, 1),
|
||||
OpcodeInfo.validOpcode("MSTORE8", 0x53, 2, 0, 1),
|
||||
OpcodeInfo.validOpcode("SLOAD", 0x54, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("SSTORE", 0x55, 2, 0, 1),
|
||||
OpcodeInfo.invalidOpcode("JUMP", 0x56),
|
||||
OpcodeInfo.invalidOpcode("JUMPI", 0x57),
|
||||
OpcodeInfo.invalidOpcode("PC", 0x58),
|
||||
OpcodeInfo.validOpcode("MSIZE", 0x59, 0, 1, 1),
|
||||
OpcodeInfo.invalidOpcode("GAS", 0x5a),
|
||||
OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST
|
||||
OpcodeInfo.validOpcode("TLOAD", 0x5c, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("TSTORE", 0x5d, 2, 0, 1),
|
||||
OpcodeInfo.validOpcode("MCOPY", 0x5e, 3, 0, 1),
|
||||
OpcodeInfo.validOpcode("PUSH0", 0x5f, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("PUSH1", 0x60, 0, 1, 2),
|
||||
OpcodeInfo.validOpcode("PUSH2", 0x61, 0, 1, 3),
|
||||
OpcodeInfo.validOpcode("PUSH3", 0x62, 0, 1, 4),
|
||||
OpcodeInfo.validOpcode("PUSH4", 0x63, 0, 1, 5),
|
||||
OpcodeInfo.validOpcode("PUSH5", 0x64, 0, 1, 6),
|
||||
OpcodeInfo.validOpcode("PUSH6", 0x65, 0, 1, 7),
|
||||
OpcodeInfo.validOpcode("PUSH7", 0x66, 0, 1, 8),
|
||||
OpcodeInfo.validOpcode("PUSH8", 0x67, 0, 1, 9),
|
||||
OpcodeInfo.validOpcode("PUSH9", 0x68, 0, 1, 10),
|
||||
OpcodeInfo.validOpcode("PUSH10", 0x69, 0, 1, 11),
|
||||
OpcodeInfo.validOpcode("PUSH11", 0x6a, 0, 1, 12),
|
||||
OpcodeInfo.validOpcode("PUSH12", 0x6b, 0, 1, 13),
|
||||
OpcodeInfo.validOpcode("PUSH13", 0x6c, 0, 1, 14),
|
||||
OpcodeInfo.validOpcode("PUSH14", 0x6d, 0, 1, 15),
|
||||
OpcodeInfo.validOpcode("PUSH15", 0x6e, 0, 1, 16),
|
||||
OpcodeInfo.validOpcode("PUSH16", 0x6f, 0, 1, 17),
|
||||
OpcodeInfo.validOpcode("PUSH17", 0x70, 0, 1, 18),
|
||||
OpcodeInfo.validOpcode("PUSH18", 0x71, 0, 1, 19),
|
||||
OpcodeInfo.validOpcode("PUSH19", 0x72, 0, 1, 20),
|
||||
OpcodeInfo.validOpcode("PUSH20", 0x73, 0, 1, 21),
|
||||
OpcodeInfo.validOpcode("PUSH21", 0x74, 0, 1, 22),
|
||||
OpcodeInfo.validOpcode("PUSH22", 0x75, 0, 1, 23),
|
||||
OpcodeInfo.validOpcode("PUSH23", 0x76, 0, 1, 24),
|
||||
OpcodeInfo.validOpcode("PUSH24", 0x77, 0, 1, 25),
|
||||
OpcodeInfo.validOpcode("PUSH25", 0x78, 0, 1, 26),
|
||||
OpcodeInfo.validOpcode("PUSH26", 0x79, 0, 1, 27),
|
||||
OpcodeInfo.validOpcode("PUSH27", 0x7a, 0, 1, 28),
|
||||
OpcodeInfo.validOpcode("PUSH28", 0x7b, 0, 1, 29),
|
||||
OpcodeInfo.validOpcode("PUSH29", 0x7c, 0, 1, 30),
|
||||
OpcodeInfo.validOpcode("PUSH30", 0x7d, 0, 1, 31),
|
||||
OpcodeInfo.validOpcode("PUSH31", 0x7e, 0, 1, 32),
|
||||
OpcodeInfo.validOpcode("PUSH32", 0x7f, 0, 1, 33),
|
||||
OpcodeInfo.validOpcode("DUP1", 0x80, 1, 2, 1),
|
||||
OpcodeInfo.validOpcode("DUP2", 0x81, 2, 3, 1),
|
||||
OpcodeInfo.validOpcode("DUP3", 0x82, 3, 4, 1),
|
||||
OpcodeInfo.validOpcode("DUP4", 0x83, 4, 5, 1),
|
||||
OpcodeInfo.validOpcode("DUP5", 0x84, 5, 6, 1),
|
||||
OpcodeInfo.validOpcode("DUP6", 0x85, 6, 7, 1),
|
||||
OpcodeInfo.validOpcode("DUP7", 0x86, 7, 8, 1),
|
||||
OpcodeInfo.validOpcode("DUP8", 0x87, 8, 9, 1),
|
||||
OpcodeInfo.validOpcode("DUP9", 0x88, 9, 10, 1),
|
||||
OpcodeInfo.validOpcode("DUP10", 0x89, 10, 11, 1),
|
||||
OpcodeInfo.validOpcode("DUP11", 0x8a, 11, 12, 1),
|
||||
OpcodeInfo.validOpcode("DUP12", 0x8b, 12, 13, 1),
|
||||
OpcodeInfo.validOpcode("DUP13", 0x8c, 13, 14, 1),
|
||||
OpcodeInfo.validOpcode("DUP14", 0x8d, 14, 15, 1),
|
||||
OpcodeInfo.validOpcode("DUP15", 0x8e, 15, 16, 1),
|
||||
OpcodeInfo.validOpcode("DUP16", 0x8f, 16, 17, 1),
|
||||
OpcodeInfo.validOpcode("SWAP1", 0x90, 2, 2, 1),
|
||||
OpcodeInfo.validOpcode("SWAP2", 0x91, 3, 3, 1),
|
||||
OpcodeInfo.validOpcode("SWAP3", 0x92, 4, 4, 1),
|
||||
OpcodeInfo.validOpcode("SWAP4", 0x93, 5, 5, 1),
|
||||
OpcodeInfo.validOpcode("SWAP5", 0x94, 6, 6, 1),
|
||||
OpcodeInfo.validOpcode("SWAP6", 0x95, 7, 7, 1),
|
||||
OpcodeInfo.validOpcode("SWAP7", 0x96, 8, 8, 1),
|
||||
OpcodeInfo.validOpcode("SWAP8", 0x97, 9, 9, 1),
|
||||
OpcodeInfo.validOpcode("SWAP9", 0x98, 10, 10, 1),
|
||||
OpcodeInfo.validOpcode("SWAP10", 0x99, 11, 11, 1),
|
||||
OpcodeInfo.validOpcode("SWAP11", 0x9a, 12, 12, 1),
|
||||
OpcodeInfo.validOpcode("SWAP12", 0x9b, 13, 13, 1),
|
||||
OpcodeInfo.validOpcode("SWAP13", 0x9c, 14, 14, 1),
|
||||
OpcodeInfo.validOpcode("SWAP14", 0x9d, 15, 15, 1),
|
||||
OpcodeInfo.validOpcode("SWAP15", 0x9e, 16, 16, 1),
|
||||
OpcodeInfo.validOpcode("SWAP16", 0x9f, 17, 17, 1),
|
||||
OpcodeInfo.validOpcode("LOG0", 0xa0, 2, 0, 1),
|
||||
OpcodeInfo.validOpcode("LOG1", 0xa1, 3, 0, 1),
|
||||
OpcodeInfo.validOpcode("LOG2", 0xa2, 4, 0, 1),
|
||||
OpcodeInfo.validOpcode("LOG3", 0xa3, 5, 0, 1),
|
||||
OpcodeInfo.validOpcode("LOG4", 0xa4, 6, 0, 1),
|
||||
OpcodeInfo.unallocatedOpcode(0xa5),
|
||||
OpcodeInfo.unallocatedOpcode(0xa6),
|
||||
OpcodeInfo.unallocatedOpcode(0xa7),
|
||||
OpcodeInfo.unallocatedOpcode(0xa8),
|
||||
OpcodeInfo.unallocatedOpcode(0xa9),
|
||||
OpcodeInfo.unallocatedOpcode(0xaa),
|
||||
OpcodeInfo.unallocatedOpcode(0xab),
|
||||
OpcodeInfo.unallocatedOpcode(0xac),
|
||||
OpcodeInfo.unallocatedOpcode(0xad),
|
||||
OpcodeInfo.unallocatedOpcode(0xae),
|
||||
OpcodeInfo.unallocatedOpcode(0xaf),
|
||||
OpcodeInfo.unallocatedOpcode(0xb0),
|
||||
OpcodeInfo.unallocatedOpcode(0xb1),
|
||||
OpcodeInfo.unallocatedOpcode(0xb2),
|
||||
OpcodeInfo.unallocatedOpcode(0xb3),
|
||||
OpcodeInfo.unallocatedOpcode(0xb4),
|
||||
OpcodeInfo.unallocatedOpcode(0xb5),
|
||||
OpcodeInfo.unallocatedOpcode(0xb6),
|
||||
OpcodeInfo.unallocatedOpcode(0xb7),
|
||||
OpcodeInfo.unallocatedOpcode(0xb8),
|
||||
OpcodeInfo.unallocatedOpcode(0xb9),
|
||||
OpcodeInfo.unallocatedOpcode(0xba),
|
||||
OpcodeInfo.unallocatedOpcode(0xbb),
|
||||
OpcodeInfo.unallocatedOpcode(0xbc),
|
||||
OpcodeInfo.unallocatedOpcode(0xbd),
|
||||
OpcodeInfo.unallocatedOpcode(0xbe),
|
||||
OpcodeInfo.unallocatedOpcode(0xbf),
|
||||
OpcodeInfo.unallocatedOpcode(0xc0),
|
||||
OpcodeInfo.unallocatedOpcode(0xc1),
|
||||
OpcodeInfo.unallocatedOpcode(0xc2),
|
||||
OpcodeInfo.unallocatedOpcode(0xc3),
|
||||
OpcodeInfo.unallocatedOpcode(0xc4),
|
||||
OpcodeInfo.unallocatedOpcode(0xc5),
|
||||
OpcodeInfo.unallocatedOpcode(0xc6),
|
||||
OpcodeInfo.unallocatedOpcode(0xc7),
|
||||
OpcodeInfo.unallocatedOpcode(0xc8),
|
||||
OpcodeInfo.unallocatedOpcode(0xc9),
|
||||
OpcodeInfo.unallocatedOpcode(0xca),
|
||||
OpcodeInfo.unallocatedOpcode(0xcb),
|
||||
OpcodeInfo.unallocatedOpcode(0xcc),
|
||||
OpcodeInfo.unallocatedOpcode(0xcd),
|
||||
OpcodeInfo.unallocatedOpcode(0xce),
|
||||
OpcodeInfo.unallocatedOpcode(0xcf),
|
||||
OpcodeInfo.validOpcode("DATALOAD", 0xd0, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("DATALOADN", 0xd1, 0, 1, 3),
|
||||
OpcodeInfo.validOpcode("DATASIZE", 0xd2, 0, 1, 1),
|
||||
OpcodeInfo.validOpcode("DATACOPY", 0xd3, 3, 0, 1),
|
||||
OpcodeInfo.unallocatedOpcode(0xd4),
|
||||
OpcodeInfo.unallocatedOpcode(0xd5),
|
||||
OpcodeInfo.unallocatedOpcode(0xd6),
|
||||
OpcodeInfo.unallocatedOpcode(0xd7),
|
||||
OpcodeInfo.unallocatedOpcode(0xd8),
|
||||
OpcodeInfo.unallocatedOpcode(0xd9),
|
||||
OpcodeInfo.unallocatedOpcode(0xda),
|
||||
OpcodeInfo.unallocatedOpcode(0xdb),
|
||||
OpcodeInfo.unallocatedOpcode(0xdc),
|
||||
OpcodeInfo.unallocatedOpcode(0xdd),
|
||||
OpcodeInfo.unallocatedOpcode(0xde),
|
||||
OpcodeInfo.unallocatedOpcode(0xdf),
|
||||
OpcodeInfo.terminalOpcode("RJUMP", 0xe0, 0, 0, 3),
|
||||
OpcodeInfo.validOpcode("RJUMPI", 0xe1, 1, 0, 3),
|
||||
OpcodeInfo.validOpcode("RJUMPV", 0xe2, 1, 0, 2),
|
||||
OpcodeInfo.validOpcode("CALLF", 0xe3, 0, 0, 3),
|
||||
OpcodeInfo.terminalOpcode("RETF", 0xe4, 0, 0, 1),
|
||||
OpcodeInfo.terminalOpcode("JUMPF", 0xe5, 0, 0, 3),
|
||||
OpcodeInfo.validOpcode("DUPN", 0xe6, 0, 1, 2),
|
||||
OpcodeInfo.validOpcode("SWAPN", 0xe7, 0, 0, 2),
|
||||
OpcodeInfo.validOpcode("EXCHANGE", 0xe8, 0, 0, 2),
|
||||
OpcodeInfo.unallocatedOpcode(0xe9),
|
||||
OpcodeInfo.unallocatedOpcode(0xea),
|
||||
OpcodeInfo.unallocatedOpcode(0xeb),
|
||||
OpcodeInfo.validOpcode("EOFCREATE", 0xec, 4, 1, 2),
|
||||
OpcodeInfo.unallocatedOpcode(0xed),
|
||||
OpcodeInfo.terminalOpcode("RETURNCONTRACT", 0xee, 2, 1, 2),
|
||||
OpcodeInfo.unallocatedOpcode(0xef),
|
||||
OpcodeInfo.invalidOpcode("CREATE", 0xf0),
|
||||
OpcodeInfo.invalidOpcode("CALL", 0xf1),
|
||||
OpcodeInfo.invalidOpcode("CALLCODE", 0xf2),
|
||||
OpcodeInfo.terminalOpcode("RETURN", 0xf3, 2, 0, 1),
|
||||
OpcodeInfo.invalidOpcode("DELEGATECALL", 0xf4),
|
||||
OpcodeInfo.invalidOpcode("CREATE2", 0xf5),
|
||||
OpcodeInfo.unallocatedOpcode(0xf6),
|
||||
OpcodeInfo.validOpcode("RETURNDATALOAD", 0xf7, 1, 1, 1),
|
||||
OpcodeInfo.validOpcode("EXTCALL", 0xf8, 4, 1, 1),
|
||||
OpcodeInfo.validOpcode("EXTDELEGATECALL", 0xf9, 3, 1, 1),
|
||||
OpcodeInfo.invalidOpcode("STATICCALL", 0xfa),
|
||||
OpcodeInfo.validOpcode("EXTSTATICCALL", 0xfb, 3, 1, 1),
|
||||
OpcodeInfo.unallocatedOpcode(0xfc),
|
||||
OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, 1),
|
||||
OpcodeInfo.terminalOpcode("INVALID", 0xfe, 0, 0, 1),
|
||||
OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.code;
|
||||
|
||||
/**
|
||||
* A work list, allowing a DAG to be evaluated while detecting disconnected sections.
|
||||
*
|
||||
* <p>When an item is marked if it has not been marked it is added to the work list. `take()`
|
||||
* returns the fist item that has not yet been returned from a take, or `-1` if no items are
|
||||
* available. Items are added by calling `put(int)`, which is idempotent. Items can be put several
|
||||
* times but will only be taken once.
|
||||
*
|
||||
* <p>`isComplete()` checks if all items have been taken. `getFirstUnmarkedItem()` is used when
|
||||
* reporting errors to identify an unconnected item.
|
||||
*/
|
||||
class WorkList {
|
||||
boolean[] marked;
|
||||
int[] items;
|
||||
int nextIndex;
|
||||
int listEnd;
|
||||
|
||||
/**
|
||||
* Create a work list of the appropriate size. The list is empty.
|
||||
*
|
||||
* @param size number of possible items
|
||||
*/
|
||||
WorkList(final int size) {
|
||||
marked = new boolean[size];
|
||||
items = new int[size];
|
||||
nextIndex = 0;
|
||||
listEnd = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the next item, if available
|
||||
*
|
||||
* @return the item number, or -1 if no items are available.
|
||||
*/
|
||||
int take() {
|
||||
if (nextIndex > listEnd) {
|
||||
return -1;
|
||||
}
|
||||
int result = items[nextIndex];
|
||||
nextIndex++;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Have all items been taken?
|
||||
*
|
||||
* @return true if all items were marked and then taken
|
||||
*/
|
||||
boolean isComplete() {
|
||||
return nextIndex >= items.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an item in the work list. This is idempotent, an item will only be added on the first call.
|
||||
*
|
||||
* @param item the item to add to the list.
|
||||
*/
|
||||
void put(final int item) {
|
||||
if (!marked[item]) {
|
||||
listEnd++;
|
||||
items[listEnd] = item;
|
||||
marked[item] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks the taken list and returns the first unmarked item
|
||||
*
|
||||
* @return the first unmarked item, or -1 if all items are marked.
|
||||
*/
|
||||
int getFirstUnmarkedItem() {
|
||||
for (int i = 0; i < marked.length; i++) {
|
||||
if (!marked[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class CachedInvalidCodeRule implements ContractValidationRule {
|
||||
@Override
|
||||
public Optional<ExceptionalHaltReason> validate(
|
||||
final Bytes contractCode, final MessageFrame frame) {
|
||||
final Code code = CodeFactory.createCode(contractCode, maxEofVersion, false);
|
||||
final Code code = CodeFactory.createCode(contractCode, maxEofVersion);
|
||||
if (!code.isValid()) {
|
||||
return Optional.of(ExceptionalHaltReason.INVALID_CODE);
|
||||
} else {
|
||||
|
||||
@@ -35,11 +35,9 @@ public class EOFValidationCodeRule implements ContractValidationRule {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EOFValidationCodeRule.class);
|
||||
|
||||
final int maxEofVersion;
|
||||
final boolean inCreateTransaction;
|
||||
|
||||
private EOFValidationCodeRule(final int maxEofVersion, final boolean inCreateTransaction) {
|
||||
private EOFValidationCodeRule(final int maxEofVersion) {
|
||||
this.maxEofVersion = maxEofVersion;
|
||||
this.inCreateTransaction = inCreateTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,13 +51,13 @@ public class EOFValidationCodeRule implements ContractValidationRule {
|
||||
@Override
|
||||
public Optional<ExceptionalHaltReason> validate(
|
||||
final Bytes contractCode, final MessageFrame frame) {
|
||||
Code code = CodeFactory.createCode(contractCode, maxEofVersion, inCreateTransaction);
|
||||
Code code = CodeFactory.createCode(contractCode, maxEofVersion);
|
||||
if (!code.isValid()) {
|
||||
LOG.trace("EOF Validation Error: {}", ((CodeInvalid) code).getInvalidReason());
|
||||
return Optional.of(ExceptionalHaltReason.INVALID_CODE);
|
||||
}
|
||||
|
||||
if (frame.getCode().getEofVersion() > code.getEofVersion()) {
|
||||
if (frame.getCode().getEofVersion() != code.getEofVersion()) {
|
||||
LOG.trace(
|
||||
"Cannot deploy older eof versions: initcode version - {} runtime code version - {}",
|
||||
frame.getCode().getEofVersion(),
|
||||
@@ -74,11 +72,9 @@ public class EOFValidationCodeRule implements ContractValidationRule {
|
||||
* Create EOF validation.
|
||||
*
|
||||
* @param maxEofVersion Maximum EOF version to validate
|
||||
* @param inCreateTransaction Is this inside a create transaction?
|
||||
* @return The EOF validation contract validation rule.
|
||||
*/
|
||||
public static ContractValidationRule of(
|
||||
final int maxEofVersion, final boolean inCreateTransaction) {
|
||||
return new EOFValidationCodeRule(maxEofVersion, inCreateTransaction);
|
||||
public static ContractValidationRule of(final int maxEofVersion) {
|
||||
return new EOFValidationCodeRule(maxEofVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +161,12 @@ public class EVMExecutor {
|
||||
case SHANGHAI -> shanghai(chainId, evmConfiguration);
|
||||
case CANCUN -> cancun(chainId, evmConfiguration);
|
||||
case PRAGUE -> prague(chainId, evmConfiguration);
|
||||
case PRAGUE_EOF -> pragueEOF(chainId, evmConfiguration);
|
||||
case OSAKA -> osaka(chainId, evmConfiguration);
|
||||
case AMSTERDAM -> amsterdam(chainId, evmConfiguration);
|
||||
case BOGOTA -> bogota(chainId, evmConfiguration);
|
||||
case POLIS -> polis(chainId, evmConfiguration);
|
||||
case BANGKOK -> bangkok(chainId, evmConfiguration);
|
||||
case FUTURE_EIPS -> futureEips(chainId, evmConfiguration);
|
||||
case EXPERIMENTAL_EIPS -> experimentalEips(chainId, evmConfiguration);
|
||||
};
|
||||
@@ -503,6 +507,21 @@ public class EVMExecutor {
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate PragueEOF evm executor.
|
||||
*
|
||||
* @param chainId the chain ID
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm executor
|
||||
*/
|
||||
public static EVMExecutor pragueEOF(
|
||||
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
|
||||
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.pragueEOF(chainId, evmConfiguration));
|
||||
executor.precompileContractRegistry =
|
||||
MainnetPrecompiledContracts.prague(executor.evm.getGasCalculator());
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate Osaka evm executor.
|
||||
*
|
||||
@@ -518,6 +537,21 @@ public class EVMExecutor {
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate Amsterdam evm executor.
|
||||
*
|
||||
* @param chainId the chain ID
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm executor
|
||||
*/
|
||||
public static EVMExecutor amsterdam(
|
||||
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
|
||||
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.amsterdam(chainId, evmConfiguration));
|
||||
executor.precompileContractRegistry =
|
||||
MainnetPrecompiledContracts.prague(executor.evm.getGasCalculator());
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate Bogota evm executor.
|
||||
*
|
||||
@@ -533,6 +567,36 @@ public class EVMExecutor {
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate Polis evm executor.
|
||||
*
|
||||
* @param chainId the chain ID
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm executor
|
||||
*/
|
||||
public static EVMExecutor polis(
|
||||
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
|
||||
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.polis(chainId, evmConfiguration));
|
||||
executor.precompileContractRegistry =
|
||||
MainnetPrecompiledContracts.prague(executor.evm.getGasCalculator());
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate Bangkok evm executor.
|
||||
*
|
||||
* @param chainId the chain ID
|
||||
* @param evmConfiguration the evm configuration
|
||||
* @return the evm executor
|
||||
*/
|
||||
public static EVMExecutor bangkok(
|
||||
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
|
||||
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.bangkok(chainId, evmConfiguration));
|
||||
executor.precompileContractRegistry =
|
||||
MainnetPrecompiledContracts.prague(executor.evm.getGasCalculator());
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate Future EIPs evm executor.
|
||||
*
|
||||
@@ -540,6 +604,7 @@ public class EVMExecutor {
|
||||
* @return the evm executor
|
||||
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
|
||||
*/
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
@InlineMe(
|
||||
replacement = "EVMExecutor.evm(EvmSpecVersion.FUTURE_EIPS, BigInteger.ONE, evmConfiguration)",
|
||||
imports = {
|
||||
@@ -672,11 +737,11 @@ public class EVMExecutor {
|
||||
final Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack();
|
||||
while (!messageFrameStack.isEmpty()) {
|
||||
final MessageFrame messageFrame = messageFrameStack.peek();
|
||||
if (messageFrame.getType() == MessageFrame.Type.CONTRACT_CREATION) {
|
||||
ccp.process(messageFrame, tracer);
|
||||
} else if (messageFrame.getType() == MessageFrame.Type.MESSAGE_CALL) {
|
||||
mcp.process(messageFrame, tracer);
|
||||
}
|
||||
(switch (messageFrame.getType()) {
|
||||
case CONTRACT_CREATION -> ccp;
|
||||
case MESSAGE_CALL -> mcp;
|
||||
})
|
||||
.process(messageFrame, tracer);
|
||||
}
|
||||
if (commitWorldState) {
|
||||
worldUpdater.commit();
|
||||
|
||||
@@ -56,24 +56,16 @@ public interface ExceptionalHaltReason {
|
||||
/** The constant PRECOMPILE_ERROR. */
|
||||
ExceptionalHaltReason PRECOMPILE_ERROR = DefaultExceptionalHaltReason.PRECOMPILE_ERROR;
|
||||
|
||||
/** The constant CODE_SECTION_MISSING. */
|
||||
ExceptionalHaltReason CODE_SECTION_MISSING = DefaultExceptionalHaltReason.CODE_SECTION_MISSING;
|
||||
|
||||
/** The constant INCORRECT_CODE_SECTION_RETURN_OUTPUTS. */
|
||||
ExceptionalHaltReason INCORRECT_CODE_SECTION_RETURN_OUTPUTS =
|
||||
DefaultExceptionalHaltReason.INCORRECT_CODE_SECTION_RETURN_OUTPUTS;
|
||||
|
||||
/** The constant TOO_FEW_INPUTS_FOR_CODE_SECTION. */
|
||||
ExceptionalHaltReason TOO_FEW_INPUTS_FOR_CODE_SECTION =
|
||||
DefaultExceptionalHaltReason.TOO_FEW_INPUTS_FOR_CODE_SECTION;
|
||||
|
||||
/** The constant JUMPF_STACK_MISMATCH. */
|
||||
ExceptionalHaltReason JUMPF_STACK_MISMATCH = DefaultExceptionalHaltReason.JUMPF_STACK_MISMATCH;
|
||||
|
||||
/** The constant EOF_CREATE_VERSION_INCOMPATIBLE. */
|
||||
ExceptionalHaltReason EOF_CREATE_VERSION_INCOMPATIBLE =
|
||||
DefaultExceptionalHaltReason.EOF_CREATE_VERSION_INCOMPATIBLE;
|
||||
|
||||
/** The constant NONEXISTENT_CONTAINER */
|
||||
ExceptionalHaltReason NONEXISTENT_CONTAINER = DefaultExceptionalHaltReason.NONEXISTENT_CONTAINER;
|
||||
|
||||
/** The constant ADDRESS_OUT_OF_RANGE */
|
||||
ExceptionalHaltReason ADDRESS_OUT_OF_RANGE = DefaultExceptionalHaltReason.ADDRESS_OUT_OF_RANGE;
|
||||
|
||||
/**
|
||||
* Name string.
|
||||
*
|
||||
@@ -114,21 +106,15 @@ public interface ExceptionalHaltReason {
|
||||
INVALID_CODE("Code is invalid"),
|
||||
/** The Precompile error. */
|
||||
PRECOMPILE_ERROR("Precompile error"),
|
||||
/** The Code section missing. */
|
||||
CODE_SECTION_MISSING("No code section at requested index"),
|
||||
/** The Insufficient code section return data. */
|
||||
INSUFFICIENT_CODE_SECTION_RETURN_DATA("The stack for a return "),
|
||||
/** The Incorrect code section return outputs. */
|
||||
INCORRECT_CODE_SECTION_RETURN_OUTPUTS(
|
||||
"The return of a code section does not have the correct number of outputs"),
|
||||
/** The Too few inputs for code section. */
|
||||
TOO_FEW_INPUTS_FOR_CODE_SECTION("Not enough stack items for a function call"),
|
||||
/** The Jumpf stack mismatch. */
|
||||
JUMPF_STACK_MISMATCH(
|
||||
"The stack height for a JUMPF does not match the requirements of the target section"),
|
||||
/** The Eof version incompatible. */
|
||||
EOF_CREATE_VERSION_INCOMPATIBLE(
|
||||
"EOF Code is attempting to create EOF code of an earlier version");
|
||||
"EOF Code is attempting to create EOF code of an earlier version"),
|
||||
/** Container referenced by EOFCREATE operation does not exist */
|
||||
NONEXISTENT_CONTAINER("Referenced subcontainer index does not exist (too large?)"),
|
||||
/** A given address cannot be used by EOF */
|
||||
ADDRESS_OUT_OF_RANGE("Address has more than 20 bytes and is out of range");
|
||||
|
||||
/** The Description. */
|
||||
final String description;
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.hyperledger.besu.evm.internal.Words;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.bytes.MutableBytes;
|
||||
@@ -55,9 +54,6 @@ public class Memory {
|
||||
}
|
||||
|
||||
private static RuntimeException overflow(final String v) {
|
||||
// TODO: we should probably have another specific exception so this properly end up as an
|
||||
// exceptional halt condition with a clear message (message that can indicate that if anyone
|
||||
// runs into this, he should contact us so we know it's a case we do need to handle).
|
||||
final String msg = "Memory index or length %s too large, cannot be larger than %d";
|
||||
throw new IllegalStateException(String.format(msg, v, MAX_BYTES));
|
||||
}
|
||||
@@ -180,7 +176,6 @@ public class Memory {
|
||||
*
|
||||
* @return The current number of active words stored in memory.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public int getActiveWords() {
|
||||
return activeWords;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.code.CodeSection;
|
||||
import org.hyperledger.besu.evm.internal.MemoryEntry;
|
||||
import org.hyperledger.besu.evm.internal.OperandStack;
|
||||
import org.hyperledger.besu.evm.internal.ReturnStack;
|
||||
@@ -216,6 +215,7 @@ public class MessageFrame {
|
||||
private final Supplier<ReturnStack> returnStack;
|
||||
private Bytes output = Bytes.EMPTY;
|
||||
private Bytes returnData = Bytes.EMPTY;
|
||||
private Code createdCode = null;
|
||||
private final boolean isStatic;
|
||||
|
||||
// Transaction state fields.
|
||||
@@ -277,13 +277,7 @@ public class MessageFrame {
|
||||
this.worldUpdater = worldUpdater;
|
||||
this.gasRemaining = initialGas;
|
||||
this.stack = new OperandStack(txValues.maxStackSize());
|
||||
this.returnStack =
|
||||
Suppliers.memoize(
|
||||
() -> {
|
||||
var rStack = new ReturnStack();
|
||||
rStack.push(new ReturnStack.ReturnStackItem(0, 0, 0));
|
||||
return rStack;
|
||||
});
|
||||
this.returnStack = Suppliers.memoize(ReturnStack::new);
|
||||
this.pc = code.isValid() ? code.getCodeSection(0).getEntryPoint() : 0;
|
||||
this.recipient = recipient;
|
||||
this.contract = contract;
|
||||
@@ -336,71 +330,6 @@ public class MessageFrame {
|
||||
return section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call function and return exceptional halt reason.
|
||||
*
|
||||
* @param calledSection the called section
|
||||
* @return the exceptional halt reason
|
||||
*/
|
||||
public ExceptionalHaltReason callFunction(final int calledSection) {
|
||||
CodeSection info = code.getCodeSection(calledSection);
|
||||
if (info == null) {
|
||||
return ExceptionalHaltReason.CODE_SECTION_MISSING;
|
||||
} else if (stack.size() + info.getMaxStackHeight() > txValues.maxStackSize()) {
|
||||
return ExceptionalHaltReason.TOO_MANY_STACK_ITEMS;
|
||||
} else if (stack.size() < info.getInputs()) {
|
||||
return ExceptionalHaltReason.TOO_FEW_INPUTS_FOR_CODE_SECTION;
|
||||
} else {
|
||||
returnStack
|
||||
.get()
|
||||
.push(new ReturnStack.ReturnStackItem(section, pc + 2, stack.size() - info.getInputs()));
|
||||
pc = info.getEntryPoint() - 1; // will be +1ed at end of operations loop
|
||||
this.section = calledSection;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the mechanics of the JUMPF operation.
|
||||
*
|
||||
* @param section the section
|
||||
* @return the exceptional halt reason, if the jump failed
|
||||
*/
|
||||
public ExceptionalHaltReason jumpFunction(final int section) {
|
||||
CodeSection info = code.getCodeSection(section);
|
||||
if (info == null) {
|
||||
return ExceptionalHaltReason.CODE_SECTION_MISSING;
|
||||
} else if (stackSize() != peekReturnStack().getStackHeight() + info.getInputs()) {
|
||||
return ExceptionalHaltReason.JUMPF_STACK_MISMATCH;
|
||||
} else {
|
||||
pc = -1; // will be +1ed at end of operations loop
|
||||
this.section = section;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return function exceptional halt reason.
|
||||
*
|
||||
* @return the exceptional halt reason
|
||||
*/
|
||||
public ExceptionalHaltReason returnFunction() {
|
||||
CodeSection thisInfo = code.getCodeSection(this.section);
|
||||
var rStack = returnStack.get();
|
||||
var returnInfo = rStack.pop();
|
||||
if ((returnInfo.getStackHeight() + thisInfo.getOutputs()) != stack.size()) {
|
||||
return ExceptionalHaltReason.INCORRECT_CODE_SECTION_RETURN_OUTPUTS;
|
||||
} else if (rStack.isEmpty()) {
|
||||
setState(MessageFrame.State.CODE_SUCCESS);
|
||||
setOutputData(Bytes.EMPTY);
|
||||
return null;
|
||||
} else {
|
||||
this.pc = returnInfo.getPC();
|
||||
this.section = returnInfo.getCodeSectionIndex();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Deducts the remaining gas. */
|
||||
public void clearGasRemaining() {
|
||||
this.gasRemaining = 0L;
|
||||
@@ -462,6 +391,24 @@ public class MessageFrame {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the created code from CREATE* operations
|
||||
*
|
||||
* @param createdCode the code that was created
|
||||
*/
|
||||
public void setCreatedCode(final Code createdCode) {
|
||||
this.createdCode = createdCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the created code from CREATE* operations
|
||||
*
|
||||
* @return the code that was created
|
||||
*/
|
||||
public Code getCreatedCode() {
|
||||
return createdCode;
|
||||
}
|
||||
|
||||
/** Clears the output data buffer. */
|
||||
public void clearOutputData() {
|
||||
setOutputData(Bytes.EMPTY);
|
||||
@@ -1027,18 +974,6 @@ public class MessageFrame {
|
||||
return txValues.warmedUpStorage().put(address, slot, Boolean.TRUE) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an address' slot is warmed up. Is deliberately publicly exposed for access from
|
||||
* trace
|
||||
*
|
||||
* @param address the address context
|
||||
* @param slot the slot to query
|
||||
* @return whether the address/slot couple is warmed up
|
||||
*/
|
||||
public boolean isStorageWarm(final Address address, final Bytes32 slot) {
|
||||
return this.txValues.warmedUpStorage().contains(address, slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the world state.
|
||||
*
|
||||
@@ -1206,6 +1141,15 @@ public class MessageFrame {
|
||||
return txValues.messageFrameStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* The return stack used for EOF code sections.
|
||||
*
|
||||
* @return the return stack
|
||||
*/
|
||||
public ReturnStack getReturnStack() {
|
||||
return returnStack.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets exceptional halt reason.
|
||||
*
|
||||
|
||||
@@ -76,7 +76,8 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
|
||||
private static final long NEW_ACCOUNT_GAS_COST = 25_000L;
|
||||
|
||||
private static final long CREATE_OPERATION_GAS_COST = 32_000L;
|
||||
/** Yellow paper constant for the cost of creating a new contract on-chain */
|
||||
protected static final long CREATE_OPERATION_GAS_COST = 32_000L;
|
||||
|
||||
private static final long COPY_WORD_GAS_COST = 3L;
|
||||
|
||||
@@ -122,7 +123,9 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
private static final long SELF_DESTRUCT_REFUND_AMOUNT = 24_000L;
|
||||
|
||||
/** Default constructor. */
|
||||
public FrontierGasCalculator() {}
|
||||
public FrontierGasCalculator() {
|
||||
// Default Constructor, for JavaDoc lint
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreate) {
|
||||
@@ -214,21 +217,13 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
return CALL_OPERATION_BASE_GAS_COST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the gas cost to transfer funds in a call operation.
|
||||
*
|
||||
* @return the gas cost to transfer funds in a call operation
|
||||
*/
|
||||
long callValueTransferGasCost() {
|
||||
@Override
|
||||
public long callValueTransferGasCost() {
|
||||
return CALL_VALUE_TRANSFER_GAS_COST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the gas cost to create a new account.
|
||||
*
|
||||
* @return the gas cost to create a new account
|
||||
*/
|
||||
long newAccountGasCost() {
|
||||
@Override
|
||||
public long newAccountGasCost() {
|
||||
return NEW_ACCOUNT_GAS_COST;
|
||||
}
|
||||
|
||||
@@ -309,6 +304,16 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinRetainedGas() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinCalleeGas() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of gas the CREATE operation will consume.
|
||||
*
|
||||
|
||||
@@ -144,6 +144,20 @@ public interface GasCalculator {
|
||||
*/
|
||||
long callOperationBaseGasCost();
|
||||
|
||||
/**
|
||||
* Returns the gas cost to transfer funds in a call operation.
|
||||
*
|
||||
* @return the gas cost to transfer funds in a call operation
|
||||
*/
|
||||
long callValueTransferGasCost();
|
||||
|
||||
/**
|
||||
* Returns the gas cost to create a new account.
|
||||
*
|
||||
* @return the gas cost to create a new account
|
||||
*/
|
||||
long newAccountGasCost();
|
||||
|
||||
/**
|
||||
* Returns the gas cost for one of the various CALL operations.
|
||||
*
|
||||
@@ -227,6 +241,20 @@ public interface GasCalculator {
|
||||
*/
|
||||
long gasAvailableForChildCall(MessageFrame frame, long stipend, boolean transfersValue);
|
||||
|
||||
/**
|
||||
* For EXT*CALL, the minimum amount of gas the parent must retain. First described in EIP-7069
|
||||
*
|
||||
* @return MIN_RETAINED_GAS
|
||||
*/
|
||||
long getMinRetainedGas();
|
||||
|
||||
/**
|
||||
* For EXT*CALL, the minimum amount of gas that a child must receive. First described in EIP-7069
|
||||
*
|
||||
* @return MIN_CALLEE_GAS
|
||||
*/
|
||||
long getMinCalleeGas();
|
||||
|
||||
/**
|
||||
* Returns the amount of gas the CREATE operation will consume.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.gascalculator;
|
||||
|
||||
import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2;
|
||||
|
||||
/**
|
||||
* Gas Calculator for Prague
|
||||
*
|
||||
* <p>Placeholder for new gas schedule items. If Prague finalzies without changes this can be
|
||||
* removed
|
||||
*
|
||||
* <UL>
|
||||
* <LI>TBD
|
||||
* </UL>
|
||||
*/
|
||||
public class PragueEOFGasCalculator extends PragueGasCalculator {
|
||||
|
||||
static final long MIN_RETAINED_GAS = 5_000;
|
||||
static final long MIN_CALLEE_GAS = 2300;
|
||||
|
||||
/** Instantiates a new Prague Gas Calculator. */
|
||||
public PragueEOFGasCalculator() {
|
||||
this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Prague Gas Calculator
|
||||
*
|
||||
* @param maxPrecompile the max precompile
|
||||
*/
|
||||
protected PragueEOFGasCalculator(final int maxPrecompile) {
|
||||
super(maxPrecompile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinRetainedGas() {
|
||||
return MIN_RETAINED_GAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinCalleeGas() {
|
||||
return MIN_CALLEE_GAS;
|
||||
}
|
||||
}
|
||||
@@ -14,91 +14,16 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.internal;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/** The type Return stack. */
|
||||
public class ReturnStack extends FlexStack<ReturnStack.ReturnStackItem> {
|
||||
|
||||
/** The type Return stack item. */
|
||||
// Java17 convert to record
|
||||
public static final class ReturnStackItem {
|
||||
|
||||
/** The Code section index. */
|
||||
final int codeSectionIndex;
|
||||
|
||||
/** The Pc. */
|
||||
final int pc;
|
||||
|
||||
/** The Stack height. */
|
||||
final int stackHeight;
|
||||
|
||||
/**
|
||||
* Instantiates a new Return stack item.
|
||||
*
|
||||
* @param codeSectionIndex the code section index
|
||||
* @param pc the pc
|
||||
* @param stackHeight the stack height
|
||||
*/
|
||||
public ReturnStackItem(final int codeSectionIndex, final int pc, final int stackHeight) {
|
||||
this.codeSectionIndex = codeSectionIndex;
|
||||
this.pc = pc;
|
||||
this.stackHeight = stackHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets code section index.
|
||||
*
|
||||
* @return the code section index
|
||||
*/
|
||||
public int getCodeSectionIndex() {
|
||||
return codeSectionIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pc.
|
||||
*
|
||||
* @return the pc
|
||||
*/
|
||||
public int getPC() {
|
||||
return pc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets stack height.
|
||||
*
|
||||
* @return the stack height
|
||||
*/
|
||||
public int getStackHeight() {
|
||||
return stackHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ReturnStackItem that = (ReturnStackItem) o;
|
||||
return codeSectionIndex == that.codeSectionIndex
|
||||
&& pc == that.pc
|
||||
&& stackHeight == that.stackHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(codeSectionIndex, pc, stackHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReturnStackItem{"
|
||||
+ "codeSectionIndex="
|
||||
+ codeSectionIndex
|
||||
+ ", pc="
|
||||
+ pc
|
||||
+ ", stackHeight="
|
||||
+ stackHeight
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The type Return stack item.
|
||||
*
|
||||
* @param codeSectionIndex the code section index
|
||||
* @param pc the pc
|
||||
*/
|
||||
public record ReturnStackItem(int codeSectionIndex, int pc) {}
|
||||
|
||||
/**
|
||||
* Max return stack size specified in <a
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.code.CodeV0;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame.State;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
@@ -40,6 +41,9 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
protected static final OperationResult UNDERFLOW_RESPONSE =
|
||||
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
|
||||
|
||||
static final Bytes LEGACY_SUCCESS_STACK_ITEM = BYTES_ONE;
|
||||
static final Bytes LEGACY_FAILURE_STACK_ITEM = Bytes.EMPTY;
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract call operation.
|
||||
*
|
||||
@@ -158,6 +162,15 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
return frame.isStatic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the child message call is a delegate call.
|
||||
*
|
||||
* @return {@code true} if the child message call is a delegate call; otherwise {@code false}
|
||||
*/
|
||||
protected boolean isDelegate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
// manual check because some reads won't come until the "complete" step.
|
||||
@@ -184,9 +197,11 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
if (value(frame).compareTo(balance) > 0 || frame.getDepth() >= 1024) {
|
||||
frame.expandMemory(inputDataOffset(frame), inputDataLength(frame));
|
||||
frame.expandMemory(outputDataOffset(frame), outputDataLength(frame));
|
||||
// For the following, we either increment the gas or return zero so weo don't get double
|
||||
// charged. If we return zero then the traces don't have the right per-opcode cost.
|
||||
frame.incrementRemainingGas(gasAvailableForChildCall(frame) + cost);
|
||||
frame.popStackItems(getStackItemsConsumed());
|
||||
frame.pushStackItem(FAILURE_STACK_ITEM);
|
||||
frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM);
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
|
||||
@@ -197,29 +212,30 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
? CodeV0.EMPTY_CODE
|
||||
: evm.getCode(contract.getCodeHash(), contract.getCode());
|
||||
|
||||
if (code.isValid()) {
|
||||
// frame addition is automatically handled by parent messageFrameStack
|
||||
MessageFrame.builder()
|
||||
.parentMessageFrame(frame)
|
||||
.type(MessageFrame.Type.MESSAGE_CALL)
|
||||
.initialGas(gasAvailableForChildCall(frame))
|
||||
.address(address(frame))
|
||||
.contract(to)
|
||||
.inputData(inputData)
|
||||
.sender(sender(frame))
|
||||
.value(value(frame))
|
||||
.apparentValue(apparentValue(frame))
|
||||
.code(code)
|
||||
.isStatic(isStatic(frame))
|
||||
.completer(child -> complete(frame, child))
|
||||
.build();
|
||||
frame.incrementRemainingGas(cost);
|
||||
|
||||
frame.setState(MessageFrame.State.CODE_SUSPENDED);
|
||||
return new OperationResult(cost, null, 0);
|
||||
} else {
|
||||
// invalid code results in a quick exit
|
||||
if (!code.isValid()) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE, 0);
|
||||
}
|
||||
|
||||
MessageFrame.builder()
|
||||
.parentMessageFrame(frame)
|
||||
.type(MessageFrame.Type.MESSAGE_CALL)
|
||||
.initialGas(gasAvailableForChildCall(frame))
|
||||
.address(address(frame))
|
||||
.contract(to)
|
||||
.inputData(inputData)
|
||||
.sender(sender(frame))
|
||||
.value(value(frame))
|
||||
.apparentValue(apparentValue(frame))
|
||||
.code(code)
|
||||
.isStatic(isStatic(frame))
|
||||
.completer(child -> complete(frame, child))
|
||||
.build();
|
||||
// see note in stack depth check about incrementing cost
|
||||
frame.incrementRemainingGas(cost);
|
||||
|
||||
frame.setState(MessageFrame.State.CODE_SUSPENDED);
|
||||
return new OperationResult(cost, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,7 +297,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
if (outputSize > outputData.size()) {
|
||||
frame.expandMemory(outputOffset, outputSize);
|
||||
frame.writeMemory(outputOffset, outputData.size(), outputData, true);
|
||||
} else {
|
||||
} else if (outputSize > 0) {
|
||||
frame.writeMemory(outputOffset, outputSize, outputData, true);
|
||||
}
|
||||
|
||||
@@ -294,13 +310,20 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
frame.incrementRemainingGas(gasRemaining);
|
||||
|
||||
frame.popStackItems(getStackItemsConsumed());
|
||||
if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
|
||||
frame.pushStackItem(SUCCESS_STACK_ITEM);
|
||||
} else {
|
||||
frame.pushStackItem(FAILURE_STACK_ITEM);
|
||||
}
|
||||
Bytes resultItem;
|
||||
|
||||
resultItem = getCallResultStackItem(childFrame);
|
||||
frame.pushStackItem(resultItem);
|
||||
|
||||
final int currentPC = frame.getPC();
|
||||
frame.setPC(currentPC + 1);
|
||||
}
|
||||
|
||||
Bytes getCallResultStackItem(final MessageFrame childFrame) {
|
||||
if (childFrame.getState() == State.COMPLETED_SUCCESS) {
|
||||
return LEGACY_SUCCESS_STACK_ITEM;
|
||||
} else {
|
||||
return LEGACY_FAILURE_STACK_ITEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
import static org.hyperledger.besu.evm.operation.AbstractCallOperation.LEGACY_FAILURE_STACK_ITEM;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
@@ -31,6 +32,7 @@ import org.hyperledger.besu.evm.internal.Words;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** The Abstract create operation. */
|
||||
@@ -40,8 +42,15 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|
||||
protected static final OperationResult UNDERFLOW_RESPONSE =
|
||||
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
|
||||
|
||||
/** The constant UNDERFLOW_RESPONSE. */
|
||||
protected static final OperationResult INVALID_OPERATION =
|
||||
new OperationResult(0L, ExceptionalHaltReason.INVALID_OPERATION);
|
||||
|
||||
/** The maximum init code size */
|
||||
protected int maxInitcodeSize;
|
||||
protected final int maxInitcodeSize;
|
||||
|
||||
/** The EOF Version this create operation requires initcode to be in */
|
||||
protected final int eofVersion;
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract create operation.
|
||||
@@ -52,6 +61,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|
||||
* @param stackItemsProduced the stack items produced
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param maxInitcodeSize Maximum init code size
|
||||
* @param eofVersion the EOF version this create operation is valid in
|
||||
*/
|
||||
protected AbstractCreateOperation(
|
||||
final int opcode,
|
||||
@@ -59,19 +69,25 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|
||||
final int stackItemsConsumed,
|
||||
final int stackItemsProduced,
|
||||
final GasCalculator gasCalculator,
|
||||
final int maxInitcodeSize) {
|
||||
final int maxInitcodeSize,
|
||||
final int eofVersion) {
|
||||
super(opcode, name, stackItemsConsumed, stackItemsProduced, gasCalculator);
|
||||
this.maxInitcodeSize = maxInitcodeSize;
|
||||
this.eofVersion = eofVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
if (frame.getCode().getEofVersion() != eofVersion) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// manual check because some reads won't come until the "complete" step.
|
||||
if (frame.stackSize() < getStackItemsConsumed()) {
|
||||
return UNDERFLOW_RESPONSE;
|
||||
}
|
||||
|
||||
Supplier<Code> codeSupplier = () -> getInitCode(frame, evm);
|
||||
Supplier<Code> codeSupplier = Suppliers.memoize(() -> getInitCode(frame, evm));
|
||||
|
||||
final long cost = cost(frame, codeSupplier);
|
||||
if (frame.isStatic()) {
|
||||
@@ -85,36 +101,41 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|
||||
final MutableAccount account = frame.getWorldUpdater().getAccount(address);
|
||||
|
||||
frame.clearReturnData();
|
||||
final long inputOffset = clampedToLong(frame.getStackItem(1));
|
||||
final long inputSize = clampedToLong(frame.getStackItem(2));
|
||||
if (inputSize > maxInitcodeSize) {
|
||||
frame.popStackItems(getStackItemsConsumed());
|
||||
return new OperationResult(cost, ExceptionalHaltReason.CODE_TOO_LARGE);
|
||||
}
|
||||
|
||||
Code code = codeSupplier.get();
|
||||
|
||||
if (value.compareTo(account.getBalance()) > 0
|
||||
|| frame.getDepth() >= 1024
|
||||
|| account.getNonce() == -1
|
||||
|| codeSupplier.get() == null) {
|
||||
|| code == null
|
||||
|| code.getEofVersion() != frame.getCode().getEofVersion()) {
|
||||
fail(frame);
|
||||
} else {
|
||||
account.incrementNonce();
|
||||
|
||||
final Bytes inputData = frame.readMemory(inputOffset, inputSize);
|
||||
// Never cache CREATEx initcode. The amount of reuse is very low, and caching mostly
|
||||
// addresses disk loading delay, and we already have the code.
|
||||
Code code = evm.getCodeUncached(inputData);
|
||||
if (code.getSize() > maxInitcodeSize) {
|
||||
frame.popStackItems(getStackItemsConsumed());
|
||||
return new OperationResult(cost, ExceptionalHaltReason.CODE_TOO_LARGE);
|
||||
}
|
||||
if (!code.isValid()) {
|
||||
fail(frame);
|
||||
} else {
|
||||
|
||||
if (code.isValid() && frame.getCode().getEofVersion() <= code.getEofVersion()) {
|
||||
frame.decrementRemainingGas(cost);
|
||||
spawnChildMessage(frame, code, evm);
|
||||
frame.incrementRemainingGas(cost);
|
||||
} else {
|
||||
fail(frame);
|
||||
}
|
||||
}
|
||||
return new OperationResult(cost, null, getPcIncrement());
|
||||
}
|
||||
|
||||
return new OperationResult(cost, null);
|
||||
/**
|
||||
* How many bytes does this operation occupy?
|
||||
*
|
||||
* @return The number of bytes the operation and immediate arguments occupy
|
||||
*/
|
||||
protected int getPcIncrement() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,13 +170,14 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|
||||
final long inputSize = clampedToLong(frame.getStackItem(2));
|
||||
frame.readMutableMemory(inputOffset, inputSize);
|
||||
frame.popStackItems(getStackItemsConsumed());
|
||||
frame.pushStackItem(FAILURE_STACK_ITEM);
|
||||
frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM);
|
||||
}
|
||||
|
||||
private void spawnChildMessage(final MessageFrame parent, final Code code, final EVM evm) {
|
||||
final Wei value = Wei.wrap(parent.getStackItem(0));
|
||||
|
||||
final Address contractAddress = targetContractAddress(parent, code);
|
||||
final Bytes inputData = getInputData(parent);
|
||||
|
||||
final long childGasStipend =
|
||||
gasCalculator().gasAvailableForChildCreate(parent.getRemainingGas());
|
||||
@@ -168,7 +190,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|
||||
.initialGas(childGasStipend)
|
||||
.address(contractAddress)
|
||||
.contract(contractAddress)
|
||||
.inputData(Bytes.EMPTY)
|
||||
.inputData(inputData)
|
||||
.sender(parent.getRecipientAddress())
|
||||
.value(value)
|
||||
.apparentValue(value)
|
||||
@@ -179,11 +201,24 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|
||||
parent.setState(MessageFrame.State.CODE_SUSPENDED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input data to be appended to the EOF factory contract. For CREATE and CREATE2 this is
|
||||
* always empty
|
||||
*
|
||||
* @param frame the message frame the operation was called in
|
||||
* @return the input data as raw bytes, or `Bytes.EMPTY` if there is no aux data
|
||||
*/
|
||||
protected Bytes getInputData(final MessageFrame frame) {
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
private void complete(final MessageFrame frame, final MessageFrame childFrame, final EVM evm) {
|
||||
frame.setState(MessageFrame.State.CODE_EXECUTING);
|
||||
|
||||
Code outputCode =
|
||||
CodeFactory.createCode(childFrame.getOutputData(), evm.getMaxEOFVersion(), true);
|
||||
(childFrame.getCreatedCode() != null)
|
||||
? childFrame.getCreatedCode()
|
||||
: CodeFactory.createCode(childFrame.getOutputData(), evm.getMaxEOFVersion());
|
||||
frame.popStackItems(getStackItemsConsumed());
|
||||
|
||||
if (outputCode.isValid()) {
|
||||
@@ -198,18 +233,18 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|
||||
onSuccess(frame, createdAddress);
|
||||
} else {
|
||||
frame.setReturnData(childFrame.getOutputData());
|
||||
frame.pushStackItem(FAILURE_STACK_ITEM);
|
||||
frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM);
|
||||
onFailure(frame, childFrame.getExceptionalHaltReason());
|
||||
}
|
||||
} else {
|
||||
frame.getWorldUpdater().deleteAccount(childFrame.getRecipientAddress());
|
||||
frame.setReturnData(childFrame.getOutputData());
|
||||
frame.pushStackItem(FAILURE_STACK_ITEM);
|
||||
frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM);
|
||||
onInvalid(frame, (CodeInvalid) outputCode);
|
||||
}
|
||||
|
||||
final int currentPC = frame.getPC();
|
||||
frame.setPC(currentPC + 1);
|
||||
frame.setPC(currentPC + getPcIncrement());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.code.CodeV0;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/**
|
||||
* A skeleton class for implementing call operations.
|
||||
*
|
||||
* <p>A call operation creates a child message call from the current message context, allows it to
|
||||
* execute, and then updates the current message context based on its execution.
|
||||
*/
|
||||
public abstract class AbstractExtCallOperation extends AbstractCallOperation {
|
||||
|
||||
static final int STACK_TO = 0;
|
||||
|
||||
/** EXT*CALL response indicating success */
|
||||
public static final Bytes EOF1_SUCCESS_STACK_ITEM = Bytes.EMPTY;
|
||||
|
||||
/** EXT*CALL response indicating a "soft failure" */
|
||||
public static final Bytes EOF1_EXCEPTION_STACK_ITEM = BYTES_ONE;
|
||||
|
||||
/** EXT*CALL response indicating a hard failure, such as a REVERT was called */
|
||||
public static final Bytes EOF1_FAILURE_STACK_ITEM = Bytes.of(2);
|
||||
|
||||
/**
|
||||
* Instantiates a new Abstract call operation.
|
||||
*
|
||||
* @param opcode the opcode
|
||||
* @param name the name
|
||||
* @param stackItemsConsumed the stack items consumed
|
||||
* @param stackItemsProduced the stack items produced
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
AbstractExtCallOperation(
|
||||
final int opcode,
|
||||
final String name,
|
||||
final int stackItemsConsumed,
|
||||
final int stackItemsProduced,
|
||||
final GasCalculator gasCalculator) {
|
||||
super(opcode, name, stackItemsConsumed, stackItemsProduced, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address to(final MessageFrame frame) {
|
||||
return Words.toAddress(frame.getStackItem(STACK_TO));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long gas(final MessageFrame frame) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long outputDataOffset(final MessageFrame frame) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long outputDataLength(final MessageFrame frame) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long gasAvailableForChildCall(final MessageFrame frame) {
|
||||
throw new UnsupportedOperationException("EXTCALL does not use gasAvailableForChildCall");
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
final Bytes toBytes = frame.getStackItem(STACK_TO).trimLeadingZeros();
|
||||
final Wei value = value(frame);
|
||||
final boolean zeroValue = value.isZero();
|
||||
long inputOffset = inputDataOffset(frame);
|
||||
long inputLength = inputDataLength(frame);
|
||||
|
||||
if (!zeroValue && isStatic(frame)) {
|
||||
return new OperationResult(
|
||||
gasCalculator().callValueTransferGasCost(), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
|
||||
}
|
||||
if (toBytes.size() > Address.SIZE) {
|
||||
return new OperationResult(
|
||||
gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputLength)
|
||||
+ (zeroValue ? 0 : gasCalculator().callValueTransferGasCost())
|
||||
+ gasCalculator().getColdAccountAccessCost(),
|
||||
ExceptionalHaltReason.ADDRESS_OUT_OF_RANGE);
|
||||
}
|
||||
Address to = Words.toAddress(toBytes);
|
||||
final Account contract = frame.getWorldUpdater().get(to);
|
||||
boolean accountCreation = contract == null && !zeroValue;
|
||||
long cost =
|
||||
gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputLength)
|
||||
+ (zeroValue ? 0 : gasCalculator().callValueTransferGasCost())
|
||||
+ (frame.warmUpAddress(to)
|
||||
? gasCalculator().getWarmStorageReadCost()
|
||||
: gasCalculator().getColdAccountAccessCost())
|
||||
+ (accountCreation ? gasCalculator().newAccountGasCost() : 0);
|
||||
long currentGas = frame.getRemainingGas() - cost;
|
||||
if (currentGas < 0) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
final Code code =
|
||||
contract == null
|
||||
? CodeV0.EMPTY_CODE
|
||||
: evm.getCode(contract.getCodeHash(), contract.getCode());
|
||||
|
||||
// invalid code results in a quick exit
|
||||
if (!code.isValid()) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE, 0);
|
||||
}
|
||||
|
||||
// last exceptional failure, prepare for call or soft failures
|
||||
frame.clearReturnData();
|
||||
|
||||
// delegate calls to prior EOF versions are prohibited
|
||||
if (isDelegate() && frame.getCode().getEofVersion() != code.getEofVersion()) {
|
||||
return softFailure(frame, cost);
|
||||
}
|
||||
|
||||
long retainedGas = Math.max(currentGas / 64, gasCalculator().getMinRetainedGas());
|
||||
long childGas = currentGas - retainedGas;
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(frame.getRecipientAddress());
|
||||
final Wei balance = (zeroValue || account == null) ? Wei.ZERO : account.getBalance();
|
||||
|
||||
// There myst be a minimum gas for a call to have access to.
|
||||
if (childGas < gasCalculator().getMinRetainedGas()) {
|
||||
return softFailure(frame, cost);
|
||||
}
|
||||
// transferring value you don't have is not a halting exception, just a failure
|
||||
if (!zeroValue && (value.compareTo(balance) > 0)) {
|
||||
return softFailure(frame, cost);
|
||||
}
|
||||
// stack too deep, for large gas systems.
|
||||
if (frame.getDepth() >= 1024) {
|
||||
return softFailure(frame, cost);
|
||||
}
|
||||
|
||||
// all checks passed, do the call
|
||||
final Bytes inputData = frame.readMutableMemory(inputOffset, inputLength);
|
||||
|
||||
MessageFrame.builder()
|
||||
.parentMessageFrame(frame)
|
||||
.type(MessageFrame.Type.MESSAGE_CALL)
|
||||
.initialGas(childGas)
|
||||
.address(address(frame))
|
||||
.contract(to)
|
||||
.inputData(inputData)
|
||||
.sender(sender(frame))
|
||||
.value(value(frame))
|
||||
.apparentValue(apparentValue(frame))
|
||||
.code(code)
|
||||
.isStatic(isStatic(frame))
|
||||
.completer(child -> complete(frame, child))
|
||||
.build();
|
||||
|
||||
frame.setState(MessageFrame.State.CODE_SUSPENDED);
|
||||
return new OperationResult(cost + childGas, null, 0);
|
||||
}
|
||||
|
||||
private @Nonnull OperationResult softFailure(final MessageFrame frame, final long cost) {
|
||||
frame.popStackItems(getStackItemsConsumed());
|
||||
frame.pushStackItem(EOF1_EXCEPTION_STACK_ITEM);
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
Bytes getCallResultStackItem(final MessageFrame childFrame) {
|
||||
return switch (childFrame.getState()) {
|
||||
case COMPLETED_SUCCESS -> EOF1_SUCCESS_STACK_ITEM;
|
||||
case EXCEPTIONAL_HALT -> EOF1_EXCEPTION_STACK_ITEM;
|
||||
default -> EOF1_FAILURE_STACK_ITEM;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,6 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
public abstract class AbstractOperation implements Operation {
|
||||
|
||||
static final Bytes BYTES_ONE = Bytes.of(1);
|
||||
static final Bytes SUCCESS_STACK_ITEM = BYTES_ONE;
|
||||
static final Bytes FAILURE_STACK_ITEM = Bytes.EMPTY;
|
||||
|
||||
private final int opcode;
|
||||
private final String name;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class AddModOperation extends AbstractFixedCostOperation {
|
||||
final Bytes value2 = frame.popStackItem();
|
||||
|
||||
if (value2.isZero()) {
|
||||
frame.pushStackItem(FAILURE_STACK_ITEM);
|
||||
frame.pushStackItem(Bytes.EMPTY);
|
||||
} else {
|
||||
BigInteger b0 = new BigInteger(1, value0.toArrayUnsafe());
|
||||
BigInteger b1 = new BigInteger(1, value1.toArrayUnsafe());
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.code.CodeSection;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.ReturnStack;
|
||||
|
||||
/** The Call F operation. */
|
||||
public class CallFOperation extends AbstractOperation {
|
||||
@@ -40,26 +41,18 @@ public class CallFOperation extends AbstractOperation {
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
final byte[] code = frame.getCode().getBytes().toArrayUnsafe();
|
||||
return staticOperation(frame, code, frame.getPC());
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs Call F operation.
|
||||
*
|
||||
* @param frame the frame
|
||||
* @param code the code
|
||||
* @param pc the pc
|
||||
* @return the successful operation result
|
||||
*/
|
||||
public static OperationResult staticOperation(
|
||||
final MessageFrame frame, final byte[] code, final int pc) {
|
||||
int section = readBigEndianU16(pc + 1, code);
|
||||
var exception = frame.callFunction(section);
|
||||
if (exception == null) {
|
||||
return callfSuccess;
|
||||
} else {
|
||||
return new OperationResult(callfSuccess.gasCost, exception);
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
|
||||
int pc = frame.getPC();
|
||||
int section = code.readBigEndianU16(pc + 1);
|
||||
CodeSection info = code.getCodeSection(section);
|
||||
frame.getReturnStack().push(new ReturnStack.ReturnStackItem(frame.getSection(), pc + 2));
|
||||
frame.setPC(info.getEntryPoint() - 1); // will be +1ed at end of operations loop
|
||||
frame.setSection(section);
|
||||
|
||||
return callfSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class Create2Operation extends AbstractCreateOperation {
|
||||
* @param maxInitcodeSize Maximum init code size
|
||||
*/
|
||||
public Create2Operation(final GasCalculator gasCalculator, final int maxInitcodeSize) {
|
||||
super(0xF5, "CREATE2", 4, 1, gasCalculator, maxInitcodeSize);
|
||||
super(0xF5, "CREATE2", 4, 1, gasCalculator, maxInitcodeSize, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,7 @@ public class CreateOperation extends AbstractCreateOperation {
|
||||
* @param maxInitcodeSize Maximum init code size
|
||||
*/
|
||||
public CreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) {
|
||||
super(0xF0, "CREATE", 3, 1, gasCalculator, maxInitcodeSize);
|
||||
super(0xF0, "CREATE", 3, 1, gasCalculator, maxInitcodeSize, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
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;
|
||||
|
||||
/** The Data load operation. */
|
||||
public class DataCopyOperation extends AbstractOperation {
|
||||
|
||||
/**
|
||||
* Instantiates a new Data Load operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public DataCopyOperation(final GasCalculator gasCalculator) {
|
||||
super(0xd3, "DATACOPY", 3, 1, gasCalculator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cost of data Copy operation.
|
||||
*
|
||||
* @param frame the frame
|
||||
* @param memOffset the mem offset
|
||||
* @param length the length
|
||||
* @return the long
|
||||
*/
|
||||
protected long cost(final MessageFrame frame, final long memOffset, final long length) {
|
||||
return gasCalculator().getVeryLowTierGasCost()
|
||||
+ gasCalculator().extCodeCopyOperationGasCost(frame, memOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
final int memOffset = clampedToInt(frame.popStackItem());
|
||||
final int sourceOffset = clampedToInt(frame.popStackItem());
|
||||
final int length = clampedToInt(frame.popStackItem());
|
||||
final long cost = cost(frame, memOffset, length);
|
||||
|
||||
final Bytes data = code.getData(sourceOffset, length);
|
||||
frame.writeMemory(memOffset, length, data);
|
||||
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
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;
|
||||
|
||||
/** The Data load operation. */
|
||||
public class DataLoadNOperation extends AbstractFixedCostOperation {
|
||||
|
||||
/** The constant OPCODE. */
|
||||
public static final int OPCODE = 0xd1;
|
||||
|
||||
/**
|
||||
* Instantiates a new Data Load operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public DataLoadNOperation(final GasCalculator gasCalculator) {
|
||||
super(OPCODE, "DATALOADN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
|
||||
int pc = frame.getPC();
|
||||
int index = code.readBigEndianU16(pc + 1);
|
||||
final Bytes data = code.getData(index, 32);
|
||||
frame.pushStackItem(data);
|
||||
frame.setPC(pc + 2);
|
||||
|
||||
return successResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
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;
|
||||
|
||||
/** The Data load operation. */
|
||||
public class DataLoadOperation extends AbstractFixedCostOperation {
|
||||
|
||||
/**
|
||||
* Instantiates a new Data Load operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public DataLoadOperation(final GasCalculator gasCalculator) {
|
||||
super(0xd0, "DATALOAD", 1, 1, gasCalculator, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation.OperationResult executeFixedCostOperation(
|
||||
final MessageFrame frame, final EVM evm) {
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
final int sourceOffset = clampedToInt(frame.popStackItem());
|
||||
|
||||
final Bytes data = code.getData(sourceOffset, 32);
|
||||
frame.pushStackItem(data);
|
||||
|
||||
return successResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
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;
|
||||
|
||||
/** The Data load operation. */
|
||||
public class DataSizeOperation extends AbstractFixedCostOperation {
|
||||
|
||||
/**
|
||||
* Instantiates a new Data Load operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public DataSizeOperation(final GasCalculator gasCalculator) {
|
||||
super(0xd2, "DATASIZE", 0, 1, gasCalculator, gasCalculator.getBaseTierGasCost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
|
||||
final Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
final int size = code.getDataSize();
|
||||
frame.pushStackItem(Bytes.ofUnsignedInt(size));
|
||||
|
||||
return successResponse;
|
||||
}
|
||||
}
|
||||
@@ -83,4 +83,9 @@ public class DelegateCallOperation extends AbstractCallOperation {
|
||||
public long gasAvailableForChildCall(final MessageFrame frame) {
|
||||
return gasCalculator().gasAvailableForChildCall(frame, gas(frame), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDelegate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
/** The Dup operation. */
|
||||
public class DupNOperation extends AbstractFixedCostOperation {
|
||||
|
||||
/** DUPN Opcode 0xe6 */
|
||||
public static final int OPCODE = 0xe6;
|
||||
|
||||
/** The Dup success operation result. */
|
||||
static final OperationResult dupSuccess = new OperationResult(3, null);
|
||||
|
||||
/**
|
||||
* Instantiates a new Dup operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public DupNOperation(final GasCalculator gasCalculator) {
|
||||
super(OPCODE, "DUPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation.OperationResult executeFixedCostOperation(
|
||||
final MessageFrame frame, final EVM evm) {
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
int pc = frame.getPC();
|
||||
|
||||
int depth = code.readU8(pc + 1);
|
||||
frame.pushStackItem(frame.getStackItem(depth));
|
||||
frame.setPC(pc + 1);
|
||||
|
||||
return dupSuccess;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import static org.hyperledger.besu.crypto.Hash.keccak256;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
/** The Create2 operation. */
|
||||
public class EOFCreateOperation extends AbstractCreateOperation {
|
||||
|
||||
/** Opcode 0xEC for operation EOFCREATE */
|
||||
public static final int OPCODE = 0xec;
|
||||
|
||||
private static final Bytes PREFIX = Bytes.fromHexString("0xFF");
|
||||
|
||||
/**
|
||||
* Instantiates a new EOFCreate operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public EOFCreateOperation(final GasCalculator gasCalculator) {
|
||||
super(OPCODE, "EOFCREATE", 4, 1, gasCalculator, Integer.MAX_VALUE, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long cost(final MessageFrame frame, final Supplier<Code> codeSupplier) {
|
||||
final int inputOffset = clampedToInt(frame.getStackItem(2));
|
||||
final int inputSize = clampedToInt(frame.getStackItem(3));
|
||||
return clampedAdd(
|
||||
gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize),
|
||||
clampedAdd(
|
||||
gasCalculator().txCreateCost(),
|
||||
gasCalculator().createKeccakCost(codeSupplier.get().getSize())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address targetContractAddress(final MessageFrame frame, final Code initcode) {
|
||||
final Address sender = frame.getRecipientAddress();
|
||||
final Bytes32 salt = Bytes32.leftPad(frame.getStackItem(1));
|
||||
final Bytes32 hash = keccak256(Bytes.concatenate(PREFIX, sender, salt, initcode.getCodeHash()));
|
||||
final Address address = Address.extract(hash);
|
||||
frame.warmUpAddress(address);
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Code getInitCode(final MessageFrame frame, final EVM evm) {
|
||||
final Code code = frame.getCode();
|
||||
int startIndex = frame.getPC() + 1;
|
||||
final int initContainerIndex = code.readU8(startIndex);
|
||||
|
||||
return code.getSubContainer(initContainerIndex, null).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bytes getInputData(final MessageFrame frame) {
|
||||
final long inputOffset = clampedToLong(frame.getStackItem(2));
|
||||
final long inputSize = clampedToLong(frame.getStackItem(3));
|
||||
return frame.readMemory(inputOffset, inputSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPcIncrement() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
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;
|
||||
|
||||
/** The Exchange operation. */
|
||||
public class ExchangeOperation extends AbstractFixedCostOperation {
|
||||
|
||||
/** EXCHANGE Opcode 0xe8 */
|
||||
public static final int OPCODE = 0xe8;
|
||||
|
||||
/** The Exchange operation success result. */
|
||||
static final OperationResult exchangeSuccess = new OperationResult(3, null);
|
||||
|
||||
/**
|
||||
* Instantiates a new Exchange operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ExchangeOperation(final GasCalculator gasCalculator) {
|
||||
super(OPCODE, "EXCHANGE", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
int pc = frame.getPC();
|
||||
int imm = code.readU8(pc + 1);
|
||||
int n = (imm >> 4) + 1;
|
||||
int m = (imm & 0x0F) + 1 + n;
|
||||
|
||||
final Bytes tmp = frame.getStackItem(n);
|
||||
frame.setStackItem(n, frame.getStackItem(m));
|
||||
frame.setStackItem(m, tmp);
|
||||
frame.setPC(pc + 1);
|
||||
|
||||
return exchangeSuccess;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
/** The Call operation. */
|
||||
public class ExtCallOperation extends AbstractExtCallOperation {
|
||||
|
||||
static final int STACK_VALUE = 1;
|
||||
static final int STACK_INPUT_OFFSET = 2;
|
||||
static final int STACK_INPUT_LENGTH = 3;
|
||||
|
||||
/**
|
||||
* Instantiates a new Call operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ExtCallOperation(final GasCalculator gasCalculator) {
|
||||
super(0xF8, "EXTCALL", 4, 1, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Wei value(final MessageFrame frame) {
|
||||
return Wei.wrap(frame.getStackItem(STACK_VALUE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Wei apparentValue(final MessageFrame frame) {
|
||||
return value(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long inputDataOffset(final MessageFrame frame) {
|
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_OFFSET));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long inputDataLength(final MessageFrame frame) {
|
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address address(final MessageFrame frame) {
|
||||
return to(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address sender(final MessageFrame frame) {
|
||||
return frame.getRecipientAddress();
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.code.EOFLayout;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -30,6 +31,9 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
/** The Ext code copy operation. */
|
||||
public class ExtCodeCopyOperation extends AbstractOperation {
|
||||
|
||||
/** This is the "code" legacy contracts see when copying code from an EOF contract. */
|
||||
public static final Bytes EOF_REPLACEMENT_CODE = Bytes.fromHexString("0xef00");
|
||||
|
||||
/**
|
||||
* Instantiates a new Ext code copy operation.
|
||||
*
|
||||
@@ -78,7 +82,12 @@ public class ExtCodeCopyOperation extends AbstractOperation {
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
final Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
|
||||
|
||||
frame.writeMemory(memOffset, sourceOffset, numBytes, code);
|
||||
if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) {
|
||||
frame.writeMemory(memOffset, sourceOffset, numBytes, EOF_REPLACEMENT_CODE);
|
||||
} else {
|
||||
frame.writeMemory(memOffset, sourceOffset, numBytes, code);
|
||||
}
|
||||
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.code.EOFLayout;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -29,6 +31,9 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
/** The Ext code hash operation. */
|
||||
public class ExtCodeHashOperation extends AbstractOperation {
|
||||
|
||||
// // 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5
|
||||
static final Hash EOF_REPLACEMENT_HASH = Hash.hash(ExtCodeCopyOperation.EOF_REPLACEMENT_CODE);
|
||||
|
||||
/**
|
||||
* Instantiates a new Ext code hash operation.
|
||||
*
|
||||
@@ -65,7 +70,12 @@ public class ExtCodeHashOperation extends AbstractOperation {
|
||||
if (account == null || account.isEmpty()) {
|
||||
frame.pushStackItem(Bytes.EMPTY);
|
||||
} else {
|
||||
frame.pushStackItem(account.getCodeHash());
|
||||
final Bytes code = account.getCode();
|
||||
if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) {
|
||||
frame.pushStackItem(EOF_REPLACEMENT_HASH);
|
||||
} else {
|
||||
frame.pushStackItem(account.getCodeHash());
|
||||
}
|
||||
}
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.evm.operation;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.code.EOFLayout;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -29,6 +30,8 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
/** The Ext code size operation. */
|
||||
public class ExtCodeSizeOperation extends AbstractOperation {
|
||||
|
||||
static final Bytes EOF_SIZE = Bytes.of(2);
|
||||
|
||||
/**
|
||||
* Instantiates a new Ext code size operation.
|
||||
*
|
||||
@@ -62,8 +65,18 @@ public class ExtCodeSizeOperation extends AbstractOperation {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
} else {
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
frame.pushStackItem(
|
||||
account == null ? Bytes.EMPTY : Words.intBytes(account.getCode().size()));
|
||||
Bytes codeSize;
|
||||
if (account == null) {
|
||||
codeSize = Bytes.EMPTY;
|
||||
} else {
|
||||
final Bytes code = account.getCode();
|
||||
if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) {
|
||||
codeSize = EOF_SIZE;
|
||||
} else {
|
||||
codeSize = Words.intBytes(code.size());
|
||||
}
|
||||
}
|
||||
frame.pushStackItem(codeSize);
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
} catch (final UnderflowException ufe) {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
/** The Delegate call operation. */
|
||||
public class ExtDelegateCallOperation extends AbstractExtCallOperation {
|
||||
|
||||
static final int STACK_INPUT_OFFSET = 1;
|
||||
static final int STACK_INPUT_LENGTH = 2;
|
||||
|
||||
/**
|
||||
* Instantiates a new Delegate call operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ExtDelegateCallOperation(final GasCalculator gasCalculator) {
|
||||
super(0xF9, "EXTDELEGATECALL", 3, 1, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Wei value(final MessageFrame frame) {
|
||||
return Wei.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Wei apparentValue(final MessageFrame frame) {
|
||||
return frame.getApparentValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long inputDataOffset(final MessageFrame frame) {
|
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_OFFSET));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long inputDataLength(final MessageFrame frame) {
|
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address address(final MessageFrame frame) {
|
||||
return frame.getRecipientAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address sender(final MessageFrame frame) {
|
||||
return frame.getSenderAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDelegate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
/** The Static call operation. */
|
||||
public class ExtStaticCallOperation extends AbstractExtCallOperation {
|
||||
|
||||
static final int STACK_INPUT_OFFSET = 1;
|
||||
static final int STACK_INPUT_LENGTH = 2;
|
||||
|
||||
/**
|
||||
* Instantiates a new Static call operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ExtStaticCallOperation(final GasCalculator gasCalculator) {
|
||||
super(0xFB, "EXTSTATICCALL", 3, 1, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Wei value(final MessageFrame frame) {
|
||||
return Wei.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Wei apparentValue(final MessageFrame frame) {
|
||||
return value(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long inputDataOffset(final MessageFrame frame) {
|
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_OFFSET));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long inputDataLength(final MessageFrame frame) {
|
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address address(final MessageFrame frame) {
|
||||
return to(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address sender(final MessageFrame frame) {
|
||||
return frame.getRecipientAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isStatic(final MessageFrame frame) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -27,7 +26,7 @@ public class JumpFOperation extends AbstractOperation {
|
||||
public static final int OPCODE = 0xe5;
|
||||
|
||||
/** The Jump F success operation result. */
|
||||
static final OperationResult jumpfSuccess = new OperationResult(3, null);
|
||||
static final OperationResult jumpfSuccess = new OperationResult(5, null);
|
||||
|
||||
/**
|
||||
* Instantiates a new Jump F operation.
|
||||
@@ -40,26 +39,15 @@ public class JumpFOperation extends AbstractOperation {
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
final byte[] code = frame.getCode().getBytes().toArrayUnsafe();
|
||||
return staticOperation(frame, code, frame.getPC());
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs Jump F operation.
|
||||
*
|
||||
* @param frame the frame
|
||||
* @param code the code
|
||||
* @param pc the pc
|
||||
* @return the successful operation result
|
||||
*/
|
||||
public static OperationResult staticOperation(
|
||||
final MessageFrame frame, final byte[] code, final int pc) {
|
||||
int section = readBigEndianU16(pc + 1, code);
|
||||
var exception = frame.jumpFunction(section);
|
||||
if (exception == null) {
|
||||
return jumpfSuccess;
|
||||
} else {
|
||||
return new OperationResult(jumpfSuccess.gasCost, exception);
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
int pc = frame.getPC();
|
||||
int section = code.readBigEndianU16(pc + 1);
|
||||
var info = code.getCodeSection(section);
|
||||
frame.setPC(info.getEntryPoint() - 1); // will be +1ed at end of operations loop
|
||||
frame.setSection(section);
|
||||
return jumpfSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -21,7 +22,7 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** The type Relative jump If operation. */
|
||||
public class RelativeJumpIfOperation extends RelativeJumpOperation {
|
||||
public class RelativeJumpIfOperation extends AbstractFixedCostOperation {
|
||||
|
||||
/** The constant OPCODE. */
|
||||
public static final int OPCODE = 0xe1;
|
||||
@@ -37,11 +38,16 @@ public class RelativeJumpIfOperation extends RelativeJumpOperation {
|
||||
|
||||
@Override
|
||||
protected OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
|
||||
final Bytes condition = frame.popStackItem();
|
||||
// If condition is zero (false), no jump is will be performed. Therefore, skip the rest.
|
||||
if (!condition.isZero()) {
|
||||
return super.executeFixedCostOperation(frame, evm);
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
final Bytes condition = frame.popStackItem();
|
||||
if (!condition.isZero()) {
|
||||
final int pcPostInstruction = frame.getPC() + 1;
|
||||
return new OperationResult(gasCost, null, 2 + code.readBigEndianI16(pcPostInstruction) + 1);
|
||||
} else {
|
||||
return new OperationResult(gasCost, null, 2 + 1);
|
||||
}
|
||||
return new OperationResult(gasCost, null, 2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,10 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** The type Relative jump operation. */
|
||||
public class RelativeJumpOperation extends AbstractFixedCostOperation {
|
||||
@@ -58,9 +56,11 @@ public class RelativeJumpOperation extends AbstractFixedCostOperation {
|
||||
|
||||
@Override
|
||||
protected OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
|
||||
final Bytes code = frame.getCode().getBytes();
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
final int pcPostInstruction = frame.getPC() + 1;
|
||||
return new OperationResult(
|
||||
gasCost, null, 2 + Words.readBigEndianI16(pcPostInstruction, code.toArrayUnsafe()) + 1);
|
||||
return new OperationResult(gasCost, null, 2 + code.readBigEndianI16(pcPostInstruction) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -39,36 +38,42 @@ public class RelativeJumpVectorOperation extends AbstractFixedCostOperation {
|
||||
|
||||
@Override
|
||||
protected OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
|
||||
final Bytes code = frame.getCode().getBytes();
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
int offsetCase;
|
||||
try {
|
||||
offsetCase = frame.popStackItem().toInt();
|
||||
offsetCase = frame.popStackItem().trimLeadingZeros().toInt();
|
||||
if (offsetCase < 0) {
|
||||
offsetCase = Integer.MAX_VALUE;
|
||||
}
|
||||
} catch (ArithmeticException | IllegalArgumentException ae) {
|
||||
offsetCase = Integer.MAX_VALUE;
|
||||
}
|
||||
final int vectorSize = getVectorSize(code, frame.getPC() + 1);
|
||||
final int vectorSize = getVectorSize(code.getBytes(), frame.getPC() + 1);
|
||||
int jumpDelta =
|
||||
(offsetCase < vectorSize)
|
||||
? code.readBigEndianI16(
|
||||
frame.getPC() + 2 + offsetCase * 2) // lookup delta if offset is in vector
|
||||
: 0; // if offsetCase is outside the vector the jump delta is zero / next opcode.
|
||||
return new OperationResult(
|
||||
gasCost,
|
||||
null,
|
||||
1
|
||||
+ 2 * vectorSize
|
||||
+ ((offsetCase >= vectorSize)
|
||||
? 0
|
||||
: readBigEndianI16(frame.getPC() + 2 + offsetCase * 2, code.toArrayUnsafe()))
|
||||
+ 1);
|
||||
2 // Opcode + length immediate
|
||||
+ 2 * vectorSize // vector size
|
||||
+ jumpDelta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets vector size.
|
||||
* Gets vector size. Vector size is one greater than length immediate, because (a) zero length
|
||||
* tables are useless and (b) it allows for 256 byte tables
|
||||
*
|
||||
* @param code the code
|
||||
* @param offsetCountByteIndex the offset count byte index
|
||||
* @return the vector size
|
||||
*/
|
||||
public static int getVectorSize(final Bytes code, final int offsetCountByteIndex) {
|
||||
return code.get(offsetCountByteIndex) & 0xff;
|
||||
return (code.toArrayUnsafe()[offsetCountByteIndex] & 0xff) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -38,11 +39,15 @@ public class RetFOperation extends AbstractOperation {
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
var exception = frame.returnFunction();
|
||||
if (exception == null) {
|
||||
return retfSuccess;
|
||||
} else {
|
||||
return new OperationResult(retfSuccess.gasCost, exception);
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
var rStack = frame.getReturnStack();
|
||||
var returnInfo = rStack.pop();
|
||||
frame.setPC(returnInfo.pc());
|
||||
frame.setSection(returnInfo.codeSectionIndex());
|
||||
|
||||
return retfSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** The Return operation. */
|
||||
public class ReturnContractOperation extends AbstractOperation {
|
||||
|
||||
/** Opcode of RETURNCONTRACT operation */
|
||||
public static final int OPCODE = 0xEE;
|
||||
|
||||
/**
|
||||
* Instantiates a new Return operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ReturnContractOperation(final GasCalculator gasCalculator) {
|
||||
super(OPCODE, "RETURNCONTRACT", 2, 0, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
|
||||
int pc = frame.getPC();
|
||||
int index = code.readU8(pc + 1);
|
||||
|
||||
final long from = clampedToLong(frame.popStackItem());
|
||||
final long length = clampedToLong(frame.popStackItem());
|
||||
|
||||
final long cost = gasCalculator().memoryExpansionGasCost(frame, from, length);
|
||||
if (frame.getRemainingGas() < cost) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
if (index >= code.getSubcontainerCount()) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.NONEXISTENT_CONTAINER);
|
||||
}
|
||||
|
||||
Bytes auxData = frame.readMemory(from, length);
|
||||
Optional<Code> newCode = code.getSubContainer(index, auxData);
|
||||
if (newCode.isEmpty()) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.NONEXISTENT_CONTAINER);
|
||||
}
|
||||
|
||||
frame.setCreatedCode(newCode.get());
|
||||
frame.setState(MessageFrame.State.CODE_SUCCESS);
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
}
|
||||
@@ -51,13 +51,15 @@ public class ReturnDataCopyOperation extends AbstractOperation {
|
||||
final Bytes returnData = frame.getReturnData();
|
||||
final int returnDataLength = returnData.size();
|
||||
|
||||
try {
|
||||
final long end = Math.addExact(sourceOffset, numBytes);
|
||||
if (end > returnDataLength) {
|
||||
return INVALID_RETURN_DATA_BUFFER_ACCESS;
|
||||
if (frame.getCode().getEofVersion() < 1) {
|
||||
try {
|
||||
final long end = Math.addExact(sourceOffset, numBytes);
|
||||
if (end > returnDataLength) {
|
||||
return INVALID_RETURN_DATA_BUFFER_ACCESS;
|
||||
}
|
||||
} catch (final ArithmeticException ae) {
|
||||
return OUT_OF_BOUNDS;
|
||||
}
|
||||
} catch (final ArithmeticException ae) {
|
||||
return OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
final long cost = gasCalculator().dataCopyOperationGasCost(frame, memOffset, numBytes);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
|
||||
|
||||
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 Return data copy operation. */
|
||||
public class ReturnDataLoadOperation extends AbstractOperation {
|
||||
|
||||
/**
|
||||
* Instantiates a new Return data copy operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ReturnDataLoadOperation(final GasCalculator gasCalculator) {
|
||||
super(0xf7, "RETURNDATALOAD", 3, 0, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
final int offset = clampedToInt(frame.popStackItem());
|
||||
Bytes returnData = frame.getReturnData();
|
||||
int retunDataSize = returnData.size();
|
||||
|
||||
Bytes value;
|
||||
if (offset > retunDataSize) {
|
||||
value = Bytes.EMPTY;
|
||||
} else if (offset + 32 >= returnData.size()) {
|
||||
value = Bytes32.rightPad(returnData.slice(offset));
|
||||
} else {
|
||||
value = returnData.slice(offset, 32);
|
||||
}
|
||||
|
||||
frame.pushStackItem(value);
|
||||
return new OperationResult(3L, null);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,9 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
/** The Stop operation. */
|
||||
public class StopOperation extends AbstractFixedCostOperation {
|
||||
|
||||
/** Opcode of STOP operation */
|
||||
public static final int OPCODE = 0x00;
|
||||
|
||||
/** The Stop operation success result. */
|
||||
static final OperationResult stopSuccess = new OperationResult(0, null);
|
||||
|
||||
@@ -32,7 +35,7 @@ public class StopOperation extends AbstractFixedCostOperation {
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public StopOperation(final GasCalculator gasCalculator) {
|
||||
super(0x00, "STOP", 0, 0, gasCalculator, gasCalculator.getZeroTierGasCost());
|
||||
super(OPCODE, "STOP", 0, 0, gasCalculator, gasCalculator.getZeroTierGasCost());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.operation;
|
||||
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
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;
|
||||
|
||||
/** The SwapN operation. */
|
||||
public class SwapNOperation extends AbstractFixedCostOperation {
|
||||
|
||||
/** SWAPN Opcode 0xe7 */
|
||||
public static final int OPCODE = 0xe7;
|
||||
|
||||
/** The Swap operation success result. */
|
||||
static final OperationResult swapSuccess = new OperationResult(3, null);
|
||||
|
||||
/**
|
||||
* Instantiates a new SwapN operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public SwapNOperation(final GasCalculator gasCalculator) {
|
||||
super(OPCODE, "SWAPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation.OperationResult executeFixedCostOperation(
|
||||
final MessageFrame frame, final EVM evm) {
|
||||
Code code = frame.getCode();
|
||||
if (code.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
int pc = frame.getPC();
|
||||
int index = code.readU8(pc + 1);
|
||||
|
||||
final Bytes tmp = frame.getStackItem(0);
|
||||
frame.setStackItem(0, frame.getStackItem(index + 1));
|
||||
frame.setStackItem(index + 1, tmp);
|
||||
frame.setPC(pc + 1);
|
||||
|
||||
return swapSuccess;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.OverflowException;
|
||||
import org.hyperledger.besu.evm.internal.UnderflowException;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
@@ -50,8 +49,6 @@ public class TLoadOperation extends AbstractOperation {
|
||||
}
|
||||
} catch (final UnderflowException ufe) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
|
||||
} catch (final OverflowException ofe) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,12 +243,12 @@ public abstract class AbstractMessageProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets code from evm, skipping the code cache
|
||||
* Gets code from evm, with handling for EOF code plus calldata
|
||||
*
|
||||
* @param codeBytes the code bytes
|
||||
* @return the code from evm
|
||||
*/
|
||||
public Code getCodeFromEVMUncached(final Bytes codeBytes) {
|
||||
return evm.getCodeUncached(codeBytes);
|
||||
public Code getCodeFromEVMForCreation(final Bytes codeBytes) {
|
||||
return evm.getCodeForCreation(codeBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,8 @@ public class ContractCreationProcessor extends AbstractMessageProcessor {
|
||||
|
||||
@Override
|
||||
public void codeSuccess(final MessageFrame frame, final OperationTracer operationTracer) {
|
||||
final Bytes contractCode = frame.getOutputData();
|
||||
final Bytes contractCode =
|
||||
frame.getCreatedCode() == null ? frame.getOutputData() : frame.getCreatedCode().getBytes();
|
||||
|
||||
final long depositFee = gasCalculator.codeDepositGasCost(contractCode.size());
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.evm.tracing;
|
||||
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
|
||||
import org.hyperledger.besu.evm.code.OpcodeInfo;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.operation.AbstractCallOperation;
|
||||
@@ -48,6 +49,7 @@ public class StandardJsonTracer implements OperationTracer {
|
||||
private Bytes memory;
|
||||
private int memorySize;
|
||||
private int depth;
|
||||
private int subdepth;
|
||||
private String storageString;
|
||||
|
||||
/**
|
||||
@@ -135,6 +137,7 @@ public class StandardJsonTracer implements OperationTracer {
|
||||
memory = null;
|
||||
}
|
||||
depth = messageFrame.getMessageStackSize();
|
||||
subdepth = messageFrame.returnStackSize();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (showStorage) {
|
||||
@@ -181,10 +184,22 @@ public class StandardJsonTracer implements OperationTracer {
|
||||
final StringBuilder sb = new StringBuilder(1024);
|
||||
sb.append("{");
|
||||
sb.append("\"pc\":").append(pc).append(",");
|
||||
if (section > 0) {
|
||||
boolean eofContract = messageFrame.getCode().getEofVersion() > 0;
|
||||
if (eofContract) {
|
||||
sb.append("\"section\":").append(section).append(",");
|
||||
}
|
||||
sb.append("\"op\":").append(opcode).append(",");
|
||||
OpcodeInfo opInfo = OpcodeInfo.getOpcode(opcode);
|
||||
if (eofContract && opInfo.pcAdvance() > 1) {
|
||||
var immediate =
|
||||
messageFrame
|
||||
.getCode()
|
||||
.getBytes()
|
||||
.slice(
|
||||
pc + messageFrame.getCode().getCodeSection(0).getEntryPoint() + 1,
|
||||
opInfo.pcAdvance() - 1);
|
||||
sb.append("\"immediate\":\"").append(immediate.toHexString()).append("\",");
|
||||
}
|
||||
sb.append("\"gas\":\"").append(gas).append("\",");
|
||||
sb.append("\"gasCost\":\"").append(shortNumber(thisGasCost)).append("\",");
|
||||
if (memory != null) {
|
||||
@@ -198,6 +213,9 @@ public class StandardJsonTracer implements OperationTracer {
|
||||
sb.append("\"returnData\":\"").append(returnData.toHexString()).append("\",");
|
||||
}
|
||||
sb.append("\"depth\":").append(depth).append(",");
|
||||
if (subdepth > 1) {
|
||||
sb.append("\"subdepth\":").append(subdepth).append(",");
|
||||
}
|
||||
sb.append("\"refund\":").append(messageFrame.getGasRefund()).append(",");
|
||||
sb.append("\"opName\":\"").append(currentOp.getName()).append("\"");
|
||||
if (executeResult.getHaltReason() != null) {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class EOFTestConstants {
|
||||
|
||||
public static final Bytes INNER_CONTRACT =
|
||||
bytesFromPrettyPrint(
|
||||
"""
|
||||
# EOF
|
||||
ef0001 # Magic and Version ( 1 )
|
||||
010004 # Types length ( 4 )
|
||||
020001 # Total code sections ( 1 )
|
||||
0009 # Code section 0 , 9 bytes
|
||||
030001 # Total subcontainers ( 1 )
|
||||
0014 # Sub container 0, 20 byte
|
||||
040000 # Data section length( 0 )
|
||||
00 # Terminator (end of header)
|
||||
# Code section 0 types
|
||||
00 # 0 inputs\s
|
||||
80 # 0 outputs (Non-returning function)
|
||||
0003 # max stack: 3
|
||||
# Code section 0
|
||||
5f # [0] PUSH0
|
||||
35 # [1] CALLDATALOAD
|
||||
5f # [2] PUSH0
|
||||
5f # [3] PUSH0
|
||||
a1 # [4] LOG1
|
||||
5f # [5] PUSH0
|
||||
5f # [6] PUSH0
|
||||
ee00 # [7] RETURNCONTRACT(0)
|
||||
# Subcontainer 0 starts here
|
||||
ef0001 # Magic and Version ( 1 )
|
||||
010004 # Types length ( 4 )
|
||||
020001 # Total code sections ( 1 )
|
||||
0001 # Code section 0 , 1 bytes
|
||||
040000 # Data section length( 0 )
|
||||
00 # Terminator (end of header)
|
||||
# Code section 0 types
|
||||
00 # 0 inputs
|
||||
80 # 0 outputs (Non-returning function)
|
||||
0000 # max stack: 0
|
||||
# Code section 0
|
||||
00 # [0] STOP
|
||||
""");
|
||||
|
||||
public static Bytes EOF_CREATE_CONTRACT =
|
||||
bytesFromPrettyPrint(
|
||||
String.format(
|
||||
"""
|
||||
ef0001 # Magic and Version ( 1 )
|
||||
010004 # Types length ( 4 )
|
||||
020001 # Total code sections ( 1 )
|
||||
000e # Code section 0 , 14 bytes
|
||||
030001 # Total subcontainers ( 1 )
|
||||
%04x # Subcontainer 0 size ?
|
||||
040000 # Data section length( 0 )
|
||||
00 # Terminator (end of header)
|
||||
# Code section 0 types
|
||||
00 # 0 inputs\s
|
||||
80 # 0 outputs (Non-returning function)
|
||||
0004 # max stack: 4
|
||||
# Code section 0
|
||||
61c0de # [0] PUSH2(0xc0de)
|
||||
5f # [3] PUSH0
|
||||
52 # [4] MSTORE
|
||||
6002 # [5] PUSH1(2)
|
||||
601e # [7] PUSH1 30
|
||||
5f # [9] PUSH0
|
||||
5f # [10] PUSH0
|
||||
ec00 # [11] EOFCREATE(0)
|
||||
00 # [13] STOP
|
||||
# Data section (empty)
|
||||
%s # subcontainer
|
||||
""",
|
||||
INNER_CONTRACT.size(), INNER_CONTRACT.toUnprefixedHexString()));
|
||||
|
||||
public static Bytes bytesFromPrettyPrint(final String prettyPrint) {
|
||||
return Bytes.fromHexString(prettyPrint.replaceAll("#.*?\n", "").replaceAll("\\s", ""));
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,12 @@ class CodeFactoryTest {
|
||||
|
||||
@Test
|
||||
void invalidCodeIncompleteMagic() {
|
||||
invalidCode("0xEF");
|
||||
invalidCode("0xEF", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidCodeInvalidMagic() {
|
||||
invalidCode("0xEFFF0101000302000400600000AABBCCDD");
|
||||
invalidCode("0xEFFF0101000302000400600000AABBCCDD", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,7 +179,12 @@ class CodeFactoryTest {
|
||||
}
|
||||
|
||||
private static void invalidCode(final String str) {
|
||||
Code code = CodeFactory.createCode(Bytes.fromHexString(str), 1, true);
|
||||
Code code = CodeFactory.createCode(Bytes.fromHexString(str), 1);
|
||||
assertThat(code.isValid()).isFalse();
|
||||
}
|
||||
|
||||
private static void invalidCode(final String str, final boolean legacy) {
|
||||
Code code = CodeFactory.createCode(Bytes.fromHexString(str), 1, legacy, false);
|
||||
assertThat(code.isValid()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user