"Small" Ethereum Object Format (EIP-3540 + EIP-3670) (#4644)

Implement "Small" EOF - EIP-3540 (container) and EIP-3670 (validation).

Make code an interface so EOF specific features are compartmentalized,
including an 'invalid' code type representing a code block that didn't
pass validation, CodeV1 for EOF1, and CodeV0 which represents pre-EOF
code. EVMs track a maximum supported EOF version (where 0 is pre-eof)and
code is generated from a CodeFactory taking in context (is it a CREATE
operation and max code size) for the validation.

Includes spec versions for "Shanghai" and transient testnet "Shandong".
"Small" EOF is only activated in Shandong.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
pull/4687/head
Danno Ferrin 2 years ago committed by GitHub
parent f940878f98
commit f20b4b3bd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      CHANGELOG.md
  2. 13
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/MergeProtocolScheduleTest.java
  3. 10
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java
  4. 17
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java
  5. 12
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  6. 12
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  7. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java
  8. 11
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java
  9. 1
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotIsolationTests.java
  10. 8
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/EVMTest.java
  11. 7
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/Create2OperationTest.java
  12. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/CreateOperationTest.java
  13. 48
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperationTest.java
  14. 8
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/Push0OperationTest.java
  15. 17
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java
  16. 58
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  17. 31
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java
  18. 42
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestCode.java
  19. 3
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java
  20. 4
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java
  21. 1
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java
  22. 140
      evm/src/main/java/org/hyperledger/besu/evm/Code.java
  23. 39
      evm/src/main/java/org/hyperledger/besu/evm/EVM.java
  24. 63
      evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java
  25. 56
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  26. 81
      evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java
  27. 74
      evm/src/main/java/org/hyperledger/besu/evm/code/CodeInvalid.java
  28. 148
      evm/src/main/java/org/hyperledger/besu/evm/code/CodeV0.java
  29. 93
      evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java
  30. 173
      evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java
  31. 327
      evm/src/main/java/org/hyperledger/besu/evm/code/OpcodesV1.java
  32. 38
      evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java
  33. 14
      evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java
  34. 4
      evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java
  35. 5
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java
  36. 66
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java
  37. 2
      evm/src/main/java/org/hyperledger/besu/evm/operation/CodeCopyOperation.java
  38. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/Push0Operation.java
  39. 6
      evm/src/main/java/org/hyperledger/besu/evm/operation/PushOperation.java
  40. 4
      evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java
  41. 15
      evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java
  42. 113
      evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java
  43. 35
      evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java
  44. 147
      evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java
  45. 4
      evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java
  46. 12
      evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java

@ -6,13 +6,16 @@
### Additions and Improvements
- Explain and improve price validation for London and local transactions during block proposal selection [#4602](https://github.com/hyperledger/besu/pull/4602)
- Support for ephemeral testnet Shandong. EIPs are still in flux, besu does not fully sync yet, and the network is subject to restarts. [#//FIXME](https://github.com/hyperledger/besu/pull///FIXME)
- Support for ephemeral testnet Shandong, for EOF testing. [#4599](https://github.com/hyperledger/besu/pull/4599)
- Improve performance of block processing by parallelizing some parts during the "commit" step [#4635](https://github.com/hyperledger/besu/pull/4635)
- Upgrade RocksDB version from 7.6.0 to 7.7.3
- Added new RPC endpoints `debug_setHead` & `debug_replayBlock [4580](https://github.com/hyperledger/besu/pull/4580)
- Upgrade OpenTelemetry to version 1.19.0 [#3675](https://github.com/hyperledger/besu/pull/3675)
- Backward sync log UX improvements [#4655](https://github.com/hyperledger/besu/pull/4655)
- Backward sync: use retry switching peer when fetching data from peers [#4656](https://github.com/hyperledger/besu/pull/4656)
- Shanghai implementation of EIP-3651 Warm coinbase [#4620](https://github.com/hyperledger/besu/pull/4620)
- Shanghai implementation of EIP-3855 Push0 [#4660](https://github.com/hyperledger/besu/pull/4660)
- Shanghai implementation of EIP-3540 and EIP-3670 Ethereum Object Format and Code Validation [#4644](https://github.com/hyperledger/besu/pull/4644)
### Bug Fixes

@ -14,7 +14,7 @@
*/
package org.hyperledger.besu.consensus.merge;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions;
@ -22,7 +22,7 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.operation.PrevRanDaoOperation;
import org.apache.tuweni.bytes.Bytes;
@ -45,7 +45,7 @@ public class MergeProtocolScheduleTest {
final ProtocolSpec homesteadSpec = protocolSchedule.getByBlockNumber(1);
final ProtocolSpec londonSpec = protocolSchedule.getByBlockNumber(1559);
assertThat(homesteadSpec.equals(londonSpec)).isFalse();
assertThat(homesteadSpec).isNotEqualTo(londonSpec);
assertThat(homesteadSpec.getFeeMarket().implementsBaseFee()).isFalse();
assertThat(londonSpec.getFeeMarket().implementsBaseFee()).isTrue();
}
@ -58,10 +58,13 @@ public class MergeProtocolScheduleTest {
assertThat(london.getName()).isEqualTo("Frontier");
assertThat(london.getBlockReward()).isEqualTo(Wei.ZERO);
assertThat(london.isSkipZeroBlockRewards()).isEqualTo(true);
assertThat(london.isSkipZeroBlockRewards()).isTrue();
Bytes diffOp = Bytes.fromHexString("0x44");
var op = london.getEvm().operationAtOffset(Code.createLegacyCode(diffOp, Hash.hash(diffOp)), 0);
var op =
london
.getEvm()
.operationAtOffset(CodeFactory.createCode(diffOp, Hash.hash(diffOp), 0, false), 0);
assertThat(op).isInstanceOf(PrevRanDaoOperation.class);
}
}

@ -354,7 +354,7 @@ public class FlatTraceGenerator {
// set value for contract creation TXes, CREATE, and CREATE2
if (actionBuilder.getCallType() == null && traceFrame.getMaybeCode().isPresent()) {
actionBuilder.init(traceFrame.getMaybeCode().get().getBytes().toHexString());
actionBuilder.init(traceFrame.getMaybeCode().get().getContainerBytes().toHexString());
resultBuilder.code(outputData.toHexString());
if (currentContext.isCreateOp()) {
// this is from a CREATE/CREATE2, so add code deposit cost.
@ -427,7 +427,7 @@ public class FlatTraceGenerator {
}
private static String firstNonNull(final String defaultValue, final String... values) {
for (String value : values) {
for (final String value : values) {
if (value != null) {
return value;
}
@ -460,7 +460,7 @@ public class FlatTraceGenerator {
traceFrame
.getMaybeCode()
.map(Code::getBytes)
.map(Code::getContainerBytes)
.map(Bytes::toHexString)
.ifPresent(subTraceActionBuilder::init);
@ -576,8 +576,8 @@ public class FlatTraceGenerator {
final TransactionTrace transactionTrace,
final TraceFrame traceFrame) {
long gasRemainingBeforeProcessed;
long gasRemainingAfterProcessed;
final long gasRemainingBeforeProcessed;
final long gasRemainingAfterProcessed;
long gasRefund = 0;
if (tracesContexts.size() == 1) {
gasRemainingBeforeProcessed = transactionTrace.getTraceFrames().get(0).getGasRemaining();

@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.Trace;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import java.util.ArrayDeque;
@ -72,9 +73,9 @@ public class VmTraceGenerator {
Optional<TraceFrame> nextTraceFrame =
iter.hasNext() ? Optional.of(iter.next()) : Optional.empty();
while (nextTraceFrame.isPresent()) {
final TraceFrame currentTraceFrame = nextTraceFrame.get();
final TraceFrame traceFrame = nextTraceFrame.get();
nextTraceFrame = iter.hasNext() ? Optional.of(iter.next()) : Optional.empty();
addFrame(currentTraceFrame, nextTraceFrame);
addFrame(traceFrame, nextTraceFrame);
}
}
return rootVmTrace;
@ -181,7 +182,11 @@ public class VmTraceGenerator {
code ->
op.setSub(
new VmTrace(
currentTraceFrame.getMaybeCode().get().getBytes().toHexString())));
currentTraceFrame
.getMaybeCode()
.get()
.getContainerBytes()
.toHexString())));
} else {
op.setCost(op.getCost());
op.setSub(null);
@ -286,7 +291,11 @@ public class VmTraceGenerator {
// set smart contract code
if (currentTrace != null && "0x".equals(currentTrace.getCode())) {
currentTrace.setCode(
currentTraceFrame.getMaybeCode().orElse(Code.EMPTY_CODE).getBytes().toHexString());
currentTraceFrame
.getMaybeCode()
.orElse(CodeV0.EMPTY_CODE)
.getContainerBytes()
.toHexString());
}
}

@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.contractvalidation.CachedInvalidCodeRule;
import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule;
import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule;
import org.hyperledger.besu.evm.frame.MessageFrame;
@ -648,6 +649,8 @@ public abstract class MainnetProtocolSpecs {
genesisConfigOptions.isZeroBaseFee()
? FeeMarket.zeroBaseFee(londonForkBlockNumber)
: FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas());
final int contractSizeLimit =
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
return parisDefinition(
chainId,
@ -676,6 +679,15 @@ public abstract class MainnetProtocolSpecs {
stackSizeLimit,
londonFeeMarket,
CoinbaseFeePriceCalculator.eip1559()))
.contractCreationProcessorBuilder(
(gasCalculator, evm) ->
new ContractCreationProcessor(
gasCalculator,
evm,
true,
List.of(MaxCodeSizeRule.of(contractSizeLimit), CachedInvalidCodeRule.of()),
1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
.name("Shandong");
}

@ -33,10 +33,10 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.worldstate.GoQuorumMutablePrivateWorldStateUpdater;
import org.hyperledger.besu.evm.AccessListEntry;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.EvmAccount;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
@ -71,14 +71,14 @@ public class MainnetTransactionProcessor {
private final AbstractMessageProcessor messageCallProcessor;
protected final int maxStackSize;
private final int maxStackSize;
protected final boolean clearEmptyAccounts;
private final boolean clearEmptyAccounts;
protected final boolean warmCoinbase;
protected final FeeMarket feeMarket;
protected final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator;
private final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator;
public MainnetTransactionProcessor(
final GasCalculator gasCalculator,
@ -394,7 +394,7 @@ public class MainnetTransactionProcessor {
.code(
maybeContract
.map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode()))
.orElse(Code.EMPTY_CODE))
.orElse(CodeV0.EMPTY_CODE))
.build();
}
@ -481,7 +481,7 @@ public class MainnetTransactionProcessor {
return transactionValidator;
}
protected static void clearAccountsThatAreEmpty(final WorldUpdater worldState) {
private static void clearAccountsThatAreEmpty(final WorldUpdater worldState) {
new ArrayList<>(worldState.getTouchedAccounts())
.stream().filter(Account::isEmpty).forEach(a -> worldState.deleteAccount(a.getAddress()));
}

@ -25,10 +25,10 @@ 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.ethereum.worldstate.DefaultMutablePrivateWorldStateUpdater;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.EvmAccount;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
import org.hyperledger.besu.evm.tracing.OperationTracer;
@ -167,7 +167,7 @@ public class PrivateTransactionProcessor {
.code(
maybeContract
.map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode()))
.orElse(Code.EMPTY_CODE))
.orElse(CodeV0.EMPTY_CODE))
.build();
}

@ -21,6 +21,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -36,7 +37,7 @@ import org.apache.tuweni.units.bigints.UInt256;
public class MessageFrameTestFixture {
public static final Address DEFAUT_ADDRESS = AddressHelpers.ofValue(244259721);
private final int maxStackSize = DEFAULT_MAX_STACK_SIZE;
private static final int maxStackSize = DEFAULT_MAX_STACK_SIZE;
private MessageFrame.Type type = MessageFrame.Type.MESSAGE_CALL;
private Deque<MessageFrame> messageFrameStack = new ArrayDeque<>();
@ -50,7 +51,7 @@ public class MessageFrameTestFixture {
private Wei gasPrice = Wei.ZERO;
private Wei value = Wei.ZERO;
private Bytes inputData = Bytes.EMPTY;
private Code code = Code.EMPTY_CODE;
private Code code = CodeV0.EMPTY_CODE;
private final List<UInt256> stackItems = new ArrayList<>();
private Optional<BlockHeader> blockHeader = Optional.empty();
private int depth = 0;
@ -62,7 +63,7 @@ public class MessageFrameTestFixture {
return this;
}
public MessageFrameTestFixture messageFrameStack(final Deque<MessageFrame> messageFrameStack) {
MessageFrameTestFixture messageFrameStack(final Deque<MessageFrame> messageFrameStack) {
this.messageFrameStack = messageFrameStack;
return this;
}
@ -103,7 +104,7 @@ public class MessageFrameTestFixture {
return this;
}
public MessageFrameTestFixture originator(final Address originator) {
MessageFrameTestFixture originator(final Address originator) {
this.originator = originator;
return this;
}
@ -123,7 +124,7 @@ public class MessageFrameTestFixture {
return this;
}
public MessageFrameTestFixture inputData(final Bytes inputData) {
MessageFrameTestFixture inputData(final Bytes inputData) {
this.inputData = inputData;
return this;
}

@ -44,7 +44,6 @@ import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolCo
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.GasPricePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;

@ -21,6 +21,8 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
@ -44,13 +46,13 @@ public class EVMTest {
@Before
public void setup() {
evm = new EVM(operationRegistry, gasCalculator, EvmConfiguration.DEFAULT);
evm = new EVM(operationRegistry, gasCalculator, EvmConfiguration.DEFAULT, EvmSpecVersion.PARIS);
}
@Test
public void assertThatEndOfScriptNotExplicitlySetInCodeReturnsAVirtualOperation() {
final Bytes noEnd = Bytes.fromHexString("0x60203560003555606035604035556000");
final Code code = Code.createLegacyCode(noEnd, Hash.hash(noEnd));
final Code code = CodeFactory.createCode(noEnd, Hash.hash(noEnd), 0, false);
final Operation operation = evm.operationAtOffset(code, code.getSize());
assertThat(operation).isNotNull();
assertThat(operation.isVirtualOperation()).isTrue();
@ -59,7 +61,7 @@ public class EVMTest {
@Test
public void assertThatEndOfScriptExplicitlySetInCodeDoesNotReturnAVirtualOperation() {
final Bytes ends = Bytes.fromHexString("0x6020356000355560603560403555600000");
final Code code = Code.createLegacyCode(ends, Hash.hash(ends));
final Code code = CodeFactory.createCode(ends, Hash.hash(ends), 0, false);
when(operationRegistry.get(anyByte())).thenReturn(new StopOperation(gasCalculator));
final Operation operation = evm.operationAtOffset(code, code.getSize() - 1);
assertThat(operation).isNotNull();

@ -24,9 +24,9 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.operation.Create2Operation;
@ -142,7 +142,7 @@ public class Create2OperationTest {
.sender(Address.fromHexString(sender))
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(Code.createLegacyCode(codeBytes, Hash.hash(codeBytes)))
.code(CodeFactory.createCode(codeBytes, Hash.hash(codeBytes), 0, true))
.depth(1)
.completer(__ -> {})
.address(Address.fromHexString(sender))
@ -168,7 +168,8 @@ public class Create2OperationTest {
when(evm.getCode(any(), any()))
.thenAnswer(
invocation ->
Code.createLegacyCode(invocation.getArgument(1), invocation.getArgument(0)));
CodeFactory.createCode(
invocation.getArgument(1), invocation.getArgument(0), 0, true));
}
@Test

@ -25,10 +25,10 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
@ -190,7 +190,7 @@ public class CreateOperationTest {
.sender(Address.fromHexString(SENDER))
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(Code.createLegacyCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE)))
.code(CodeFactory.createCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE), 0, true))
.depth(depth)
.completer(__ -> {})
.address(Address.fromHexString(SENDER))

@ -17,8 +17,6 @@ package org.hyperledger.besu.ethereum.vm.operations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
@ -28,8 +26,9 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.MessageFrameTestFixture;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
@ -45,7 +44,6 @@ import org.apache.tuweni.units.bigints.UInt256;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
@ -85,7 +83,7 @@ public class JumpOperationTest {
final OperationRegistry registry = new OperationRegistry();
registry.put(new JumpOperation(gasCalculator));
registry.put(new JumpDestOperation(gasCalculator));
evm = new EVM(registry, gasCalculator, EvmConfiguration.DEFAULT);
evm = new EVM(registry, gasCalculator, EvmConfiguration.DEFAULT, EvmSpecVersion.PARIS);
}
@Test
@ -95,7 +93,7 @@ public class JumpOperationTest {
final MessageFrame frame =
createMessageFrameBuilder(10_000L)
.pushStackItem(UInt256.fromHexString("0x03"))
.code(Code.createLegacyCode(jumpBytes, Hash.hash(jumpBytes)))
.code(CodeFactory.createCode(jumpBytes, Hash.hash(jumpBytes), 0, false))
.build();
frame.setPC(CURRENT_PC);
@ -110,7 +108,7 @@ public class JumpOperationTest {
final MessageFrame frame =
createMessageFrameBuilder(10_000L)
.pushStackItem(UInt256.fromHexString("0x03"))
.code(Code.createLegacyCode(jumpBytes, Hash.hash(jumpBytes)))
.code(CodeFactory.createCode(jumpBytes, Hash.hash(jumpBytes), 0, false))
.build();
frame.setPC(CURRENT_PC);
@ -125,7 +123,7 @@ public class JumpOperationTest {
final MessageFrame frameDestinationGreaterThanCodeSize =
createMessageFrameBuilder(100L)
.pushStackItem(UInt256.fromHexString("0xFFFFFFFF"))
.code(Code.createLegacyCode(jumpBytes, Hash.hash(jumpBytes)))
.code(CodeFactory.createCode(jumpBytes, Hash.hash(jumpBytes), 0, false))
.build();
frameDestinationGreaterThanCodeSize.setPC(CURRENT_PC);
@ -135,7 +133,7 @@ public class JumpOperationTest {
final MessageFrame frameDestinationEqualsToCodeSize =
createMessageFrameBuilder(100L)
.pushStackItem(UInt256.fromHexString("0x04"))
.code(Code.createLegacyCode(badJump, Hash.hash(badJump)))
.code(CodeFactory.createCode(badJump, Hash.hash(badJump), 0, false))
.build();
frameDestinationEqualsToCodeSize.setPC(CURRENT_PC);
@ -153,41 +151,11 @@ public class JumpOperationTest {
final MessageFrame longContract =
createMessageFrameBuilder(100L)
.pushStackItem(UInt256.fromHexString("0x12c"))
.code(Code.createLegacyCode(longCode, Hash.hash(longCode)))
.code(CodeFactory.createCode(longCode, Hash.hash(longCode), 0, false))
.build();
longContract.setPC(255);
final OperationResult result = operation.execute(longContract, evm);
assertThat(result.getHaltReason()).isNull();
}
@Test
public void shouldReuseJumpDestMap() {
final JumpOperation operation = new JumpOperation(gasCalculator);
final Bytes jumpBytes = Bytes.fromHexString("0x6003565b00");
final Code getsCached = spy(Code.createLegacyCode(jumpBytes, Hash.hash(jumpBytes)));
MessageFrame frame =
createMessageFrameBuilder(10_000L)
.pushStackItem(UInt256.fromHexString("0x03"))
.code(getsCached)
.build();
frame.setPC(CURRENT_PC);
OperationResult result = operation.execute(frame, evm);
assertThat(result.getHaltReason()).isNull();
Mockito.verify(getsCached, times(1)).calculateJumpDests();
// do it again to prove we don't recalc, and we hit the cache
frame =
createMessageFrameBuilder(10_000L)
.pushStackItem(UInt256.fromHexString("0x03"))
.code(getsCached)
.build();
frame.setPC(CURRENT_PC);
result = operation.execute(frame, evm);
assertThat(result.getHaltReason()).isNull();
Mockito.verify(getsCached, times(1)).calculateJumpDests();
}
}

@ -15,12 +15,12 @@
package org.hyperledger.besu.ethereum.vm.operations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.Code.EMPTY_CODE;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
@ -34,12 +34,12 @@ import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class Push0OperationTest {
class Push0OperationTest {
private final GasCalculator gasCalculator = new BerlinGasCalculator();
@Test
public void shouldPush0OntoStack() {
void shouldPush0OntoStack() {
final MessageFrame frame = createMessageFrame(100, Optional.of(Wei.of(5L)));
final Operation operation = new Push0Operation(gasCalculator);
final OperationResult result = operation.execute(frame, null);
@ -59,7 +59,7 @@ public class Push0OperationTest {
final BlockHeader blockHeader = mock(BlockHeader.class);
when(blockHeader.getBaseFee()).thenReturn(baseFee);
when(frame.getBlockValues()).thenReturn(blockHeader);
when(frame.getCode()).thenReturn(EMPTY_CODE);
when(frame.getCode()).thenReturn(CodeV0.EMPTY_CODE);
return frame;
}
}

@ -28,7 +28,6 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValue
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.worldstate.MutableWorldView;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
@ -54,7 +53,8 @@ public class BlockchainModule {
}
@Provides
MutableWorldView getMutableWorldView(
@Singleton
MutableWorldState getMutableWorldState(
@Named("StateRoot") final Bytes32 stateRoot,
final WorldStateStorage worldStateStorage,
final WorldStatePreimageStorage worldStatePreimageStorage,
@ -64,32 +64,35 @@ public class BlockchainModule {
final MutableWorldState mutableWorldState =
new DefaultMutableWorldState(worldStateStorage, worldStatePreimageStorage);
genesisState.writeStateTo(mutableWorldState);
return (MutableWorldView) mutableWorldState;
return mutableWorldState;
} else {
return (MutableWorldView)
new DefaultMutableWorldState(stateRoot, worldStateStorage, worldStatePreimageStorage);
return new DefaultMutableWorldState(stateRoot, worldStateStorage, worldStatePreimageStorage);
}
}
@Provides
@Singleton
WorldStateStorage provideWorldStateStorage(
@Named("worldState") final KeyValueStorage keyValueStorage) {
return new WorldStateKeyValueStorage(keyValueStorage);
}
@Provides
@Singleton
WorldStatePreimageStorage provideWorldStatePreimageStorage(
@Named("worldStatePreimage") final KeyValueStorage keyValueStorage) {
return new WorldStatePreimageKeyValueStorage(keyValueStorage);
}
@Provides
WorldUpdater provideWorldUpdater(final MutableWorldView mutableWorldView) {
return mutableWorldView.updater();
@Singleton
WorldUpdater provideWorldUpdater(final MutableWorldState mutableWorldState) {
return mutableWorldState.updater();
}
@Provides
@Named("StateRoot")
@Singleton
Bytes32 provideStateRoot(final BlockParameter blockParameter, final Blockchain blockchain) {
if (blockParameter.isEarliest()) {
return blockchain.getBlockHeader(0).orElseThrow().getStateRoot();

@ -159,7 +159,7 @@ public class EvmToolCommand implements Runnable {
// add sub commands here
commandLine.registerConverter(Address.class, Address::fromHexString);
commandLine.registerConverter(Bytes.class, Bytes::fromHexString);
commandLine.registerConverter(Wei.class, (arg) -> Wei.of(Long.parseUnsignedLong(arg)));
commandLine.registerConverter(Wei.class, arg -> Wei.of(Long.parseUnsignedLong(arg)));
commandLine.setExecutionStrategy(resultHandler).execute(args);
}
@ -201,12 +201,34 @@ public class EvmToolCommand implements Runnable {
.buildBlockHeader();
Log4j2ConfiguratorUtil.setAllLevels("", repeat == 0 ? Level.INFO : Level.OFF);
int repeat = this.repeat;
int remainingIters = this.repeat;
Log4j2ConfiguratorUtil.setLevel(
"org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder", Level.OFF);
final ProtocolSpec protocolSpec = component.getProtocolSpec().apply(0);
Log4j2ConfiguratorUtil.setLevel(
"org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder", null);
final Transaction tx =
new Transaction(
0,
Wei.ZERO,
Long.MAX_VALUE,
Optional.ofNullable(receiver),
Wei.ZERO,
null,
callData,
sender,
Optional.empty());
final long intrinsicGasCost =
protocolSpec
.getGasCalculator()
.transactionIntrinsicGasCost(tx.getPayload(), tx.isContractCreation());
final long accessListCost =
tx.getAccessList()
.map(list -> protocolSpec.getGasCalculator().accessListGasCost(list))
.orElse(0L);
long txGas = gas - intrinsicGasCost - accessListCost;
final PrecompileContractRegistry precompileContractRegistry =
protocolSpec.getPrecompileContractRegistry();
final EVM evm = protocolSpec.getEvm();
@ -214,7 +236,7 @@ public class EvmToolCommand implements Runnable {
final Stopwatch stopwatch = Stopwatch.createUnstarted();
long lastTime = 0;
do {
final boolean lastLoop = repeat == 0;
final boolean lastLoop = remainingIters == 0;
final OperationTracer tracer = // You should have picked Mercy.
lastLoop && showJsonResults
@ -231,7 +253,7 @@ public class EvmToolCommand implements Runnable {
.type(MessageFrame.Type.MESSAGE_CALL)
.messageFrameStack(messageFrameStack)
.worldUpdater(updater)
.initialGas(gas)
.initialGas(txGas)
.contract(Address.ZERO)
.address(receiver)
.originator(sender)
@ -269,41 +291,19 @@ public class EvmToolCommand implements Runnable {
}
if (lastLoop && messageFrameStack.isEmpty()) {
final Transaction tx =
new Transaction(
0,
Wei.ZERO,
Long.MAX_VALUE,
Optional.ofNullable(receiver),
Wei.ZERO,
null,
callData,
sender,
Optional.empty());
final long intrinsicGasCost =
protocolSpec
.getGasCalculator()
.transactionIntrinsicGasCost(tx.getPayload(), tx.isContractCreation());
final long accessListCost =
tx.getAccessList()
.map(list -> protocolSpec.getGasCalculator().accessListGasCost(list))
.orElse(0L);
final long evmGas = gas - messageFrame.getRemainingGas();
final long evmGas = txGas - messageFrame.getRemainingGas();
out.println();
out.println(
new JsonObject()
.put("gasUser", "0x" + Long.toHexString(evmGas))
.put("timens", lastTime)
.put("time", lastTime / 1000)
.put(
"gasTotal",
"0x" + Long.toHexString(evmGas + intrinsicGasCost) + accessListCost));
.put("gasTotal", "0x" + Long.toHexString(evmGas)));
}
}
lastTime = stopwatch.elapsed().toNanos();
stopwatch.reset();
} while (repeat-- > 0);
} while (remainingIters-- > 0);
} catch (final IOException e) {
LOG.error("Unable to create Genesis module", e);

@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.referencetests.GeneralStateTestCaseEipSpec;
@ -95,7 +96,9 @@ public class StateTestSubCommand implements Runnable {
private final ObjectMapper objectMapper = new ObjectMapper();
@SuppressWarnings("unused")
public StateTestSubCommand() {
// PicoCLI requires this
this(null, System.in, System.out);
}
@ -112,10 +115,10 @@ public class StateTestSubCommand implements Runnable {
@Override
public void run() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(Feature.AUTO_CLOSE_SOURCE);
final ObjectMapper stateTestMapper = new ObjectMapper();
stateTestMapper.disable(Feature.AUTO_CLOSE_SOURCE);
final JavaType javaType =
objectMapper
stateTestMapper
.getTypeFactory()
.constructParametricType(Map.class, String.class, GeneralStateTestCaseSpec.class);
try {
@ -131,13 +134,9 @@ public class StateTestSubCommand implements Runnable {
}
final File file = new File(fileName);
if (file.isFile()) {
try {
final Map<String, GeneralStateTestCaseSpec> generalStateTests =
objectMapper.readValue(file, javaType);
executeStateTest(generalStateTests);
} catch (final JsonProcessingException jpe) {
output.println("File content error: " + jpe);
}
final Map<String, GeneralStateTestCaseSpec> generalStateTests =
stateTestMapper.readValue(file, javaType);
executeStateTest(generalStateTests);
} else {
output.println("File not found: " + fileName);
}
@ -145,10 +144,12 @@ public class StateTestSubCommand implements Runnable {
} else {
for (final File stateTestFile : stateTestFiles) {
final Map<String, GeneralStateTestCaseSpec> generalStateTests =
objectMapper.readValue(stateTestFile, javaType);
stateTestMapper.readValue(stateTestFile, javaType);
executeStateTest(generalStateTests);
}
}
} catch (final JsonProcessingException jpe) {
output.println("File content error: " + jpe);
} catch (final IOException e) {
LOG.error("Unable to read state file", e);
}
@ -159,7 +160,7 @@ public class StateTestSubCommand implements Runnable {
generalStateTestEntry
.getValue()
.finalStateSpecs()
.forEach((fork, specs) -> traceTestSpecs(generalStateTestEntry.getKey(), specs));
.forEach((__, specs) -> traceTestSpecs(generalStateTestEntry.getKey(), specs));
}
}
@ -208,8 +209,8 @@ public class StateTestSubCommand implements Runnable {
throw new UnsupportedForkException(forkName);
}
final MainnetTransactionProcessor processor =
protocolSchedule.getByBlockNumber(0).getTransactionProcessor();
ProtocolSpec protocolSpec = protocolSchedule.getByBlockNumber(0);
final MainnetTransactionProcessor processor = protocolSpec.getTransactionProcessor();
final WorldUpdater worldStateUpdater = worldState.updater();
final ReferenceTestBlockchain blockchain =
new ReferenceTestBlockchain(blockHeader.getNumber());
@ -244,9 +245,9 @@ public class StateTestSubCommand implements Runnable {
final var timeNs = timer.elapsed(TimeUnit.NANOSECONDS);
final var mGps = gasUsed * 1000.0f / timeNs;
summaryLine.put("Mgps", String.format("%.3f", mGps));
summaryLine.put("gasUsed", StandardJsonTracer.shortNumber(gasUsed));
summaryLine.put("time", timeNs);
summaryLine.put("Mgps", String.format("%.3f", mGps));
// Check the world state root hash.
summaryLine.put("test", test);

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.referencetests;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeFactory;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@ -24,7 +25,9 @@ import org.apache.tuweni.bytes.Bytes;
/** A mock for representing EVM Code associated with an account. */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReferenceTestCode extends Code {
public class ReferenceTestCode implements Code {
private final Code code;
/**
* Public constructor.
@ -33,6 +36,41 @@ public class ReferenceTestCode extends Code {
*/
@JsonCreator
public ReferenceTestCode(final String bytes) {
super(Bytes.fromHexString(bytes), Hash.hash(Bytes.fromHexString(bytes)));
this.code =
CodeFactory.createCode(
Bytes.fromHexString(bytes),
Hash.hash(Bytes.fromHexString(bytes)),
CodeFactory.MAX_KNOWN_CODE_VERSION,
false);
}
@Override
public int getSize() {
return code.getSize();
}
@Override
public Bytes getCodeBytes() {
return code.getCodeBytes();
}
@Override
public Bytes getContainerBytes() {
return code.getContainerBytes();
}
@Override
public Hash getCodeHash() {
return code.getCodeHash();
}
@Override
public boolean isJumpDestInvalid(final int jumpDestination) {
return code.isJumpDestInvalid(jumpDestination);
}
@Override
public boolean isValid() {
return code.isValid();
}
}

@ -73,6 +73,9 @@ public class ReferenceTestProtocolSchedules {
builder.put(
"Merge",
createSchedule(new StubGenesisConfigOptions().mergeNetSplitBlock(0).baseFeePerGas(0x0a)));
builder.put(
"Shanghai",
createSchedule(new StubGenesisConfigOptions().shandongBlock(0).baseFeePerGas(0x0a)));
builder.put("Shandong", createSchedule(new StubGenesisConfigOptions().shandongBlock(0)));
return new ReferenceTestProtocolSchedules(builder.build());
}

@ -49,6 +49,7 @@ public class BlockchainReferenceTestTools {
"test.ethereum.blockchain.eips",
"FrontierToHomesteadAt5,HomesteadToEIP150At5,HomesteadToDaoAt5,EIP158ToByzantiumAt5,"
+ "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix,Istanbul,Berlin,"
+ "Shandong,"
+ "London,Merge,Shanghai,Cancun,Prague,Osaka,Bogota");
NETWORKS_TO_RUN = Arrays.asList(networks.split(","));
}
@ -81,7 +82,8 @@ public class BlockchainReferenceTestTools {
params.ignore("CALLBlake2f_MaxRounds.*");
params.ignore("loopMul_*");
// Inconclusive fork choice rule, since in merge CL should be choosing forks and setting the chain head.
// 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");
}

@ -61,6 +61,7 @@ public class GeneralStateReferenceTestTools {
System.getProperty(
"test.ethereum.state.eips",
"Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix,Istanbul,Berlin,"
+ "Shandong,"
+ "London,Merge,Shanghai,Cancun,Prague,Osaka,Bogota");
EIPS_TO_RUN = Arrays.asList(eips.split(","));
}

@ -15,131 +15,55 @@
package org.hyperledger.besu.evm;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.operation.JumpDestOperation;
import org.hyperledger.besu.evm.operation.PushOperation;
import com.google.common.base.MoreObjects;
import com.google.errorprone.annotations.RestrictedApi;
import org.apache.tuweni.bytes.Bytes;
/** Represents EVM code associated with an account. */
public class Code {
public static final Code EMPTY_CODE = new Code(Bytes.EMPTY, Hash.EMPTY);
/** The bytes representing the code. */
private final Bytes bytes;
/** The hash of the code, needed for accessing metadata about the bytecode */
private final Hash codeHash;
/** Used to cache valid jump destinations. */
long[] validJumpDestinations;
/** Syntactic sugar for an empty contract */
public static Code EMPTY = new Code(Bytes.EMPTY, Hash.EMPTY);
public interface Code {
/**
* Public constructor.
* Size of the code in bytes. This is for the whole container, not just the code section in
* formats that have sections.
*
* @param bytes The byte representation of the code.
* @param codeHash the Hash of the bytes in the code.
* @return size of code in bytes.
*/
protected Code(final Bytes bytes, final Hash codeHash) {
this.bytes = bytes;
this.codeHash = codeHash;
}
@RestrictedApi(
explanation = "To be used for testing purpose only",
link = "",
allowedOnPath = ".*/src/test/.*")
public static Code createLegacyCode(final Bytes bytes, final Hash codeHash) {
return new Code(bytes, codeHash);
}
int getSize();
/**
* Returns true if the object is equal to this; otherwise false.
* Gets the code bytes. For legacy code it is the whole container. For V1 it is the code section
* alone.
*
* @param other The object to compare this with.
* @return True if the object is equal to this; otherwise false.
* @return the code bytes
*/
@Override
public boolean equals(final Object other) {
if (other == null) return false;
if (other == this) return true;
if (!(other instanceof Code)) return false;
final Code that = (Code) other;
return this.bytes.equals(that.bytes);
}
@Override
public int hashCode() {
return bytes.hashCode();
}
Bytes getCodeBytes();
/**
* Size of the Code, in bytes
* 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.
*
* @return The number of bytes in the code.
* @return container bytes.
*/
public int getSize() {
return bytes.size();
}
public long[] calculateJumpDests() {
final int size = getSize();
final long[] bitmap = new long[(size >> 6) + 1];
final byte[] rawCode = getBytes().toArrayUnsafe();
final int length = rawCode.length;
for (int i = 0; i < length; ) {
long thisEntry = 0L;
final int entryPos = i >> 6;
final int max = Math.min(64, length - (entryPos << 6));
int j = i & 0x3F;
for (; j < max; i++, j++) {
final byte operationNum = rawCode[i];
if (operationNum == JumpDestOperation.OPCODE) {
thisEntry |= 1L << j;
} else if (operationNum > PushOperation.PUSH_BASE) {
// not needed - && operationNum <= PushOperation.PUSH_MAX
// Java quirk, all bytes are signed, and PUSH32 is 127, which is Byte.MAX_VALUE
// so we don't need to check the upper bound as it will never be violated
final int multiByteDataLen = operationNum - PushOperation.PUSH_BASE;
j += multiByteDataLen;
i += multiByteDataLen;
}
}
bitmap[entryPos] = thisEntry;
}
this.validJumpDestinations = bitmap;
return bitmap;
}
public Bytes getBytes() {
return bytes;
}
Bytes getContainerBytes();
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("bytes", bytes).toString();
}
public Hash getCodeHash() {
return codeHash;
}
/**
* Hash of the entire container
*
* @return hash of the code.
*/
Hash getCodeHash();
public boolean isJumpDestInvalid(final int jumpDestination) {
if (jumpDestination < 0 || jumpDestination >= getSize()) {
return true;
}
if (validJumpDestinations == null || validJumpDestinations.length == 0) {
validJumpDestinations = calculateJumpDests();
}
/**
* For V0 and V1, is the target jump location valid?
*
* @param jumpDestination index from PC=0. Code section for v1, whole container in V0
* @return true if the operation is both a valid opcode and a JUMPDEST
*/
boolean isJumpDestInvalid(final int jumpDestination);
final long targetLong = validJumpDestinations[jumpDestination >>> 6];
final long targetBit = 1L << (jumpDestination & 0x3F);
return (targetLong & targetBit) == 0L;
}
/**
* Code is considered valid by the EVM.
*
* @return isValid
*/
boolean isValid();
}

@ -18,6 +18,7 @@ import static org.hyperledger.besu.evm.operation.PushOperation.PUSH_BASE;
import static org.hyperledger.besu.evm.operation.SwapOperation.SWAP_BASE;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.frame.MessageFrame.State;
@ -39,6 +40,7 @@ import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.operation.OperationRegistry;
import org.hyperledger.besu.evm.operation.OrOperation;
import org.hyperledger.besu.evm.operation.PopOperation;
import org.hyperledger.besu.evm.operation.Push0Operation;
import org.hyperledger.besu.evm.operation.PushOperation;
import org.hyperledger.besu.evm.operation.SGtOperation;
import org.hyperledger.besu.evm.operation.SLtOperation;
@ -70,28 +72,42 @@ public class EVM {
private final GasCalculator gasCalculator;
private final Operation endOfScriptStop;
private final CodeCache codeCache;
private final EvmSpecVersion evmSpecVersion;
// Optimized operation flags
private final boolean enableShanghai;
public EVM(
final OperationRegistry operations,
final GasCalculator gasCalculator,
final EvmConfiguration evmConfiguration) {
final EvmConfiguration evmConfiguration,
final EvmSpecVersion evmSpecVersion) {
this.operations = operations;
this.gasCalculator = gasCalculator;
this.endOfScriptStop = new VirtualOperation(new StopOperation(gasCalculator));
this.codeCache = new CodeCache(evmConfiguration);
this.evmSpecVersion = evmSpecVersion;
enableShanghai = EvmSpecVersion.SHANGHAI.ordinal() <= evmSpecVersion.ordinal();
}
public GasCalculator getGasCalculator() {
return gasCalculator;
}
public int getMaxEOFVersion() {
return evmSpecVersion.maxEofVersion;
}
// Note to maintainers: lots of Java idioms and OO principals are being set aside in the
// name of performance. This is one of the hottest sections of code.
//
// Please benchmark before refactoring.
public void runToHalt(final MessageFrame frame, final OperationTracer tracing) {
evmSpecVersion.maybeWarnVersion();
var operationTracer = tracing == OperationTracer.NO_TRACING ? null : tracing;
byte[] code = frame.getCode().getBytes().toArrayUnsafe();
byte[] code = frame.getCode().getCodeBytes().toArrayUnsafe();
Operation[] operationArray = operations.getOperations();
while (frame.getState() == MessageFrame.State.CODE_EXECUTING) {
Operation currentOperation;
@ -147,14 +163,8 @@ public class EVM {
result = SignExtendOperation.staticOperation(frame);
break;
case 0x0c:
result = InvalidOperation.INVALID_RESULT;
break;
case 0x0d:
result = InvalidOperation.INVALID_RESULT;
break;
case 0x0e:
result = InvalidOperation.INVALID_RESULT;
break;
case 0x0f:
result = InvalidOperation.INVALID_RESULT;
break;
@ -191,6 +201,12 @@ public class EVM {
case 0x50: // POP
result = PopOperation.staticOperation(frame);
break;
case 0x5f: // PUSH0
result =
enableShanghai
? Push0Operation.staticOperation(frame)
: InvalidOperation.INVALID_RESULT;
break;
case 0x60: // PUSH1-32
case 0x61:
case 0x62:
@ -292,8 +308,8 @@ public class EVM {
@VisibleForTesting
public Operation operationAtOffset(final Code code, final int offset) {
final Bytes bytecode = code.getBytes();
// If the length of the program code is shorter than the offset halt execution.
final Bytes bytecode = code.getCodeBytes();
// If the length of the program code is shorter than the required offset, halt execution.
if (offset >= bytecode.size()) {
return endOfScriptStop;
}
@ -306,7 +322,8 @@ public class EVM {
public Code getCode(final Hash codeHash, final Bytes codeBytes) {
Code result = codeCache.getIfPresent(codeHash);
if (result == null) {
result = new Code(codeBytes, codeHash);
result =
CodeFactory.createCode(codeBytes, codeHash, evmSpecVersion.getMaxEofVersion(), false);
codeCache.put(codeHash, result);
}
return result;

@ -0,0 +1,63 @@
/*
* 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
public enum EvmSpecVersion {
FRONTIER(0, true),
HOMESTEAD(0, true),
BYZANTIUM(0, true),
CONSTANTINOPLE(0, true),
ISTANBUL(0, true),
LONDON(0, true),
PARIS(0, true),
SHANGHAI(1, false),
/** Transient fork, will be removed */
SHANDONG(1, false);
private static final Logger LOGGER = LoggerFactory.getLogger(EvmSpecVersion.class);
final boolean specFinalized;
final int maxEofVersion;
boolean versionWarned = false;
EvmSpecVersion(final int maxEofVersion, final boolean specFinalized) {
this.maxEofVersion = maxEofVersion;
this.specFinalized = specFinalized;
}
public int getMaxEofVersion() {
return maxEofVersion;
}
@SuppressWarnings("AlreadyChecked") // false positive
public void maybeWarnVersion() {
if (versionWarned) {
return;
}
if (!specFinalized) {
LOGGER.error(
"****** Not for Production Network Use ******\nExecuting code from EVM Spec Version {}, which has not been finalized.\n****** Not for Production Network Use ******",
this.name());
}
versionWarned = true;
}
}

@ -115,19 +115,23 @@ import org.apache.tuweni.bytes.Bytes32;
/** Provides EVMs supporting the appropriate operations for mainnet hard forks. */
public class MainnetEVMs {
public static final BigInteger DEV_NET_CHAIN_ID = BigInteger.valueOf(1337);
private MainnetEVMs() {
// utility class
}
public static final BigInteger DEV_NET_CHAIN_ID = BigInteger.valueOf(1337);
public static EVM frontier(final EvmConfiguration evmConfiguration) {
return frontier(new FrontierGasCalculator(), evmConfiguration);
}
public static EVM frontier(
final GasCalculator gasCalculator, final EvmConfiguration evmConfiguration) {
return new EVM(frontierOperations(gasCalculator), gasCalculator, evmConfiguration);
return new EVM(
frontierOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.FRONTIER);
}
public static OperationRegistry frontierOperations(final GasCalculator gasCalculator) {
@ -230,7 +234,11 @@ public class MainnetEVMs {
public static EVM homestead(
final GasCalculator gasCalculator, final EvmConfiguration evmConfiguration) {
return new EVM(homesteadOperations(gasCalculator), gasCalculator, evmConfiguration);
return new EVM(
homesteadOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.HOMESTEAD);
}
public static OperationRegistry homesteadOperations(final GasCalculator gasCalculator) {
@ -259,7 +267,11 @@ public class MainnetEVMs {
public static EVM byzantium(
final GasCalculator gasCalculator, final EvmConfiguration evmConfiguration) {
return new EVM(byzantiumOperations(gasCalculator), gasCalculator, evmConfiguration);
return new EVM(
byzantiumOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.BYZANTIUM);
}
public static OperationRegistry byzantiumOperations(final GasCalculator gasCalculator) {
@ -283,7 +295,11 @@ public class MainnetEVMs {
public static EVM constantinople(
final GasCalculator gasCalculator, final EvmConfiguration evmConfiguration) {
return new EVM(constantinopleOperations(gasCalculator), gasCalculator, evmConfiguration);
return new EVM(
constantinopleOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.CONSTANTINOPLE);
}
public static OperationRegistry constantinopleOperations(final GasCalculator gasCalculator) {
@ -318,7 +334,11 @@ public class MainnetEVMs {
final GasCalculator gasCalculator,
final BigInteger chainId,
final EvmConfiguration evmConfiguration) {
return new EVM(istanbulOperations(gasCalculator, chainId), gasCalculator, evmConfiguration);
return new EVM(
istanbulOperations(gasCalculator, chainId),
gasCalculator,
evmConfiguration,
EvmSpecVersion.ISTANBUL);
}
public static OperationRegistry istanbulOperations(
@ -359,7 +379,11 @@ public class MainnetEVMs {
final GasCalculator gasCalculator,
final BigInteger chainId,
final EvmConfiguration evmConfiguration) {
return new EVM(londonOperations(gasCalculator, chainId), gasCalculator, evmConfiguration);
return new EVM(
londonOperations(gasCalculator, chainId),
gasCalculator,
evmConfiguration,
EvmSpecVersion.LONDON);
}
public static OperationRegistry londonOperations(
@ -377,15 +401,15 @@ public class MainnetEVMs {
registry.put(new BaseFeeOperation(gasCalculator));
}
public static EVM paris(final BigInteger chainId, final EvmConfiguration evmConfiguration) {
return paris(new LondonGasCalculator(), chainId, evmConfiguration);
}
public static EVM paris(
final GasCalculator gasCalculator,
final BigInteger chainId,
final EvmConfiguration evmConfiguration) {
return new EVM(parisOperations(gasCalculator, chainId), gasCalculator, evmConfiguration);
return new EVM(
parisOperations(gasCalculator, chainId),
gasCalculator,
evmConfiguration,
EvmSpecVersion.PARIS);
}
public static OperationRegistry parisOperations(
@ -411,7 +435,11 @@ public class MainnetEVMs {
final GasCalculator gasCalculator,
final BigInteger chainId,
final EvmConfiguration evmConfiguration) {
return new EVM(shandongOperations(gasCalculator, chainId), gasCalculator, evmConfiguration);
return new EVM(
shandongOperations(gasCalculator, chainId),
gasCalculator,
evmConfiguration,
EvmSpecVersion.SHANDONG);
}
public static OperationRegistry shandongOperations(

@ -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.evm.code;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import org.apache.tuweni.bytes.Bytes;
public final class CodeFactory {
public static final byte EOF_LEAD_BYTE = -17; // 0xEF in signed byte form
public static final int MAX_KNOWN_CODE_VERSION = 1;
private CodeFactory() {
// factory class, no instantiations.
}
public static Code createCode(
final Bytes bytes,
final Hash codeHash,
final int maxEofVersion,
final boolean inCreateOperation) {
if (maxEofVersion == 0) {
return new CodeV0(bytes, codeHash);
} else if (maxEofVersion == 1) {
int codeSize = bytes.size();
if (codeSize > 0 && bytes.get(0) == EOF_LEAD_BYTE) {
if (codeSize == 1 && !inCreateOperation) {
return new CodeV0(bytes, codeHash);
}
if (codeSize < 3) {
return new CodeInvalid(codeHash, bytes, "EOF Container too short");
}
if (bytes.get(1) != 0) {
if (inCreateOperation) {
// because some 0xef code made it to mainnet, this is only an error at contract create
return new CodeInvalid(codeHash, bytes, "Incorrect second byte");
} else {
return new CodeV0(bytes, codeHash);
}
}
int version = bytes.get(2);
if (version != 1) {
return new CodeInvalid(codeHash, bytes, "Unsupported EOF Version: " + version);
}
final EOFLayout layout = EOFLayout.parseEOF(bytes);
if (!layout.isValid()) {
return new CodeInvalid(
codeHash, bytes, "Invalid EOF Layout: " + layout.getInvalidReason());
}
final long[] jumpMap =
OpcodesV1.validateAndCalculateJumpDests(layout.getSections()[EOFLayout.SECTION_CODE]);
if (jumpMap != null) {
return new CodeV1(codeHash, layout, jumpMap);
} else {
return new CodeInvalid(codeHash, bytes, "Opcode Validation Failed");
}
} else {
return new CodeV0(bytes, codeHash);
}
} else {
return new CodeInvalid(codeHash, bytes, "Unsupported max code version " + maxEofVersion);
}
}
}

@ -0,0 +1,74 @@
/*
* 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 org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import org.apache.tuweni.bytes.Bytes;
/**
* For code versions where code can be deemed "invalid" this represents a cachable instance of
* invalid code. Note that EXTCODE operations can still access invalid code.
*/
public class CodeInvalid implements Code {
private final Hash codeHash;
private final Bytes codeBytes;
private final String invalidReason;
public CodeInvalid(final Hash codeHash, final Bytes codeBytes, final String invalidReason) {
this.codeHash = codeHash;
this.codeBytes = codeBytes;
this.invalidReason = invalidReason;
}
public String getInvalidReason() {
return invalidReason;
}
@Override
public int getSize() {
return codeBytes.size();
}
@Override
public Bytes getCodeBytes() {
return getContainerBytes();
}
@Override
public Bytes getContainerBytes() {
return codeBytes;
}
@Override
public Hash getCodeHash() {
return codeHash;
}
@Override
public boolean isJumpDestInvalid(final int jumpDestination) {
return false;
}
@Override
public boolean isValid() {
return false;
}
}

@ -0,0 +1,148 @@
/*
* 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 org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.operation.JumpDestOperation;
import org.hyperledger.besu.evm.operation.PushOperation;
import com.google.common.base.MoreObjects;
import org.apache.tuweni.bytes.Bytes;
public class CodeV0 implements Code {
public static final CodeV0 EMPTY_CODE = new CodeV0(Bytes.EMPTY, Hash.EMPTY);
/** The bytes representing the code. */
private final Bytes bytes;
/** The hash of the code, needed for accessing metadata about the bytecode */
private final Hash codeHash;
/** Used to cache valid jump destinations. */
private long[] validJumpDestinations;
/**
* Public constructor.
*
* @param bytes The byte representation of the code.
* @param codeHash the Hash of the bytes in the code.
*/
CodeV0(final Bytes bytes, final Hash codeHash) {
this.bytes = bytes;
this.codeHash = codeHash;
}
/**
* 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.
*/
@Override
public boolean equals(final Object other) {
if (other == null) return false;
if (other == this) return true;
if (!(other instanceof CodeV0)) return false;
final CodeV0 that = (CodeV0) other;
return this.bytes.equals(that.bytes);
}
@Override
public int hashCode() {
return bytes.hashCode();
}
/**
* Size of the Code, in bytes
*
* @return The number of bytes in the code.
*/
@Override
public int getSize() {
return bytes.size();
}
@Override
public Bytes getCodeBytes() {
return getContainerBytes();
}
@Override
public Bytes getContainerBytes() {
return bytes;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("bytes", bytes).toString();
}
@Override
public Hash getCodeHash() {
return codeHash;
}
@Override
public boolean isJumpDestInvalid(final int jumpDestination) {
if (jumpDestination < 0 || jumpDestination >= getSize()) {
return true;
}
if (validJumpDestinations == null || validJumpDestinations.length == 0) {
validJumpDestinations = calculateJumpDests();
}
final long targetLong = validJumpDestinations[jumpDestination >>> 6];
final long targetBit = 1L << (jumpDestination & 0x3F);
return (targetLong & targetBit) == 0L;
}
@Override
public boolean isValid() {
return true;
}
long[] calculateJumpDests() {
final int size = getSize();
final long[] bitmap = new long[(size >> 6) + 1];
final byte[] rawCode = bytes.toArrayUnsafe();
final int length = rawCode.length;
for (int i = 0; i < length; ) {
long thisEntry = 0L;
final int entryPos = i >> 6;
final int max = Math.min(64, length - (entryPos << 6));
int j = i & 0x3F;
for (; j < max; i++, j++) {
final byte operationNum = rawCode[i];
if (operationNum == JumpDestOperation.OPCODE) {
thisEntry |= 1L << j;
} else if (operationNum > PushOperation.PUSH_BASE) {
// not needed - && operationNum <= PushOperation.PUSH_MAX
// Java quirk, all bytes are signed, and PUSH32 is 127, which is Byte.MAX_VALUE
// so we don't need to check the upper bound as it will never be violated
final int multiByteDataLen = operationNum - PushOperation.PUSH_BASE;
j += multiByteDataLen;
i += multiByteDataLen;
}
}
bitmap[entryPos] = thisEntry;
}
return bitmap;
}
}

@ -0,0 +1,93 @@
/*
* 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 org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import java.util.Objects;
import org.apache.tuweni.bytes.Bytes;
public class CodeV1 implements Code {
private final Hash codeHash;
private final Bytes container;
private final Bytes code;
private final long[] validJumpDestinations;
CodeV1(final Hash codeHash, final EOFLayout layout, final long[] validJumpDestinations) {
this.codeHash = codeHash;
this.container = layout.getContainer();
this.code = layout.getSections()[EOFLayout.SECTION_CODE];
this.validJumpDestinations = validJumpDestinations;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final CodeV1 codeV1 = (CodeV1) o;
return codeHash.equals(codeV1.codeHash)
&& container.equals(codeV1.container)
&& code.equals(codeV1.code);
}
@Override
public int hashCode() {
return Objects.hash(codeHash, container, code);
}
@Override
public int getSize() {
return container.size();
}
@Override
public Bytes getCodeBytes() {
return code;
}
@Override
public Bytes getContainerBytes() {
return container;
}
@Override
public Hash getCodeHash() {
return codeHash;
}
@Override
public boolean isJumpDestInvalid(final int jumpDestination) {
if (jumpDestination < 0 || jumpDestination >= getSize()) {
return true;
}
if (validJumpDestinations == null || validJumpDestinations.length == 0) {
return true;
}
final long targetLong = validJumpDestinations[jumpDestination >>> 6];
final long targetBit = 1L << (jumpDestination & 0x3F);
return (targetLong & targetBit) == 0L;
}
@Override
public boolean isValid() {
return true;
}
}

@ -0,0 +1,173 @@
/*
* 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 java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
public class EOFLayout {
static final int SECTION_CODE = 0x01;
static final int SECTION_DATA = 0x02;
static final int MAX_SUPPORTED_SECTION = SECTION_DATA;
static final int MAX_SUPPORTED_VERSION = 1;
private final Bytes container;
private final int version;
private final Bytes[] sections;
private final String invalidReason;
private EOFLayout(final Bytes container, final int version, final Bytes[] sections) {
this.container = container;
this.version = version;
this.sections = sections;
this.invalidReason = null;
}
private EOFLayout(final Bytes container, final String invalidReason) {
this.container = container;
this.version = -1;
this.sections = null;
this.invalidReason = invalidReason;
}
public static EOFLayout parseEOF(final Bytes container) {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(container.toArrayUnsafe());
if (inputStream.available() < 3) {
return new EOFLayout(container, "EOF Container too small");
}
if (inputStream.read() != 0xEF) {
return new EOFLayout(container, "EOF header byte 0 incorrect");
}
if (inputStream.read() != 0x0) {
return new EOFLayout(container, "EOF header byte 1 incorrect");
}
final int version = inputStream.read();
if (version > MAX_SUPPORTED_VERSION || version < 1) {
return new EOFLayout(container, "Unsupported EOF Version " + version);
}
final List<EOFSectionInfo> sectionInfos = new ArrayList<>(3);
EOFSectionInfo sectionInfo = EOFSectionInfo.getSectionInfo(inputStream);
while (sectionInfo != null && sectionInfo.kind() != 0) {
sectionInfos.add(sectionInfo);
sectionInfo = EOFSectionInfo.getSectionInfo(inputStream);
}
if (sectionInfo == null) {
return new EOFLayout(container, "Improper section headers");
}
int remaining = inputStream.available();
int pos = container.size() - remaining;
final Bytes[] sections = new Bytes[MAX_SUPPORTED_SECTION + 1];
for (final var info : sectionInfos) {
final int kind = info.kind();
final int size = info.size();
if (kind > MAX_SUPPORTED_SECTION) {
return new EOFLayout(container, "EOF Section kind " + kind + " not supported");
}
if (sections[kind] != null) {
return new EOFLayout(container, "Duplicate section number " + kind);
}
if (size == 0) {
return new EOFLayout(container, "Empty section contents");
}
if (size > remaining) {
return new EOFLayout(container, "Missing or incomplete section data");
}
if (kind == 1 && sections[2] != null) {
return new EOFLayout(container, "Code section cannot follow data section");
}
sections[kind] = container.slice(pos, size);
pos += size;
remaining -= size;
}
if (pos < container.size()) {
return new EOFLayout(container, "Dangling data at end of container");
}
if (sections[1] == null) {
return new EOFLayout(container, "Missing code (kind=1) section");
} else if (sections[1].size() < 1) {
return new EOFLayout(container, "Code section empty");
}
return new EOFLayout(container, version, sections);
}
public Bytes getContainer() {
return container;
}
public int getVersion() {
return version;
}
public Bytes[] getSections() {
return sections;
}
public String getInvalidReason() {
return invalidReason;
}
public boolean isValid() {
return invalidReason == null;
}
}
// TODO should be a record
final class EOFSectionInfo {
private final int kind;
private final int size;
private EOFSectionInfo(final int kind, final int size) {
this.kind = kind;
this.size = size;
}
static EOFSectionInfo getSectionInfo(final InputStream in) {
try {
final int kind = in.read();
if (kind < 0) {
return null;
} else if (kind == 0) {
return new EOFSectionInfo(kind, 0);
} else {
final int msb = in.read() << 8;
final int lsb = in.read();
return new EOFSectionInfo(kind, msb + lsb);
}
} catch (final IOException ioe) {
return null;
}
}
public int kind() {
return kind;
}
public int size() {
return size;
}
}

@ -0,0 +1,327 @@
/*
* 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 org.hyperledger.besu.evm.operation.PushOperation;
import org.apache.tuweni.bytes.Bytes;
class OpcodesV1 {
static final byte INVALID = 0x01;
static final byte VALID = 0x02;
static final byte TERMINAL = 0x04;
static final byte JUMPDEST = 0x08;
static final byte VALID_AND_TERMINAL = VALID | TERMINAL;
static final byte VALID_AND_JUMPDEST = VALID | JUMPDEST;
private static final byte[] opcodeAttributes = {
VALID_AND_TERMINAL, // 0x00 STOP
VALID, // 0x01 - ADD
VALID, // 0x02 - MUL
VALID, // 0x03 - SUB
VALID, // 0x04 - DIV
VALID, // 0x05 - SDIV
VALID, // 0x06 - MOD
VALID, // 0x07 - SMOD
VALID, // 0x08 - ADDMOD
VALID, // 0x09 - MULMOD
VALID, // 0x0a - EXP
VALID, // 0x0b - SIGNEXTEND
INVALID, // 0x0c
INVALID, // 0x0d
INVALID, // 0x0e
INVALID, // 0x0f
VALID, // 0x10 - LT
VALID, // 0x11 - GT
VALID, // 0x12 - SLT
VALID, // 0x13 - SGT
VALID, // 0x14 - EQ
VALID, // 0x15 - ISZERO
VALID, // 0x16 - AND
VALID, // 0x17 - OR
VALID, // 0x18 - XOR
VALID, // 0x19 - NOT
VALID, // 0x1a - BYTE
VALID, // 0x1b - SHL
VALID, // 0x1c - SHR
VALID, // 0x1d - SAR
INVALID, // 0x1e
INVALID, // 0x1f
VALID, // 0x20 - SHA3
INVALID, // 0x21
INVALID, // 0x22
INVALID, // 0x23
INVALID, // 0x24
INVALID, // 0x25
INVALID, // 0x26
INVALID, // 0x27
INVALID, // 0x28
INVALID, // 0x29
INVALID, // 0x2a
INVALID, // 0x2b
INVALID, // 0x2c
INVALID, // 0x2d
INVALID, // 0x2e
INVALID, // 0x2f
VALID, // 0x30 - ADDRESS
VALID, // 0x31 - BALANCE
VALID, // 0x32 - ORIGIN
VALID, // 0x33 - CALLER
VALID, // 0x34 - CALLVALUE
VALID, // 0x35 - CALLDATALOAD
VALID, // 0x36 - CALLDATASIZE
VALID, // 0x37 - CALLDATACOPY
VALID, // 0x38 - CODESIZE
VALID, // 0x39 - CODECOPY
VALID, // 0x3a - GASPRICE
VALID, // 0x3b - EXTCODESIZE
VALID, // 0x3c - EXTCODECOPY
VALID, // 0x3d - RETURNDATASIZE
VALID, // 0x3e - RETURNDATACOPY
VALID, // 0x3f - EXTCODEHASH
VALID, // 0x40 - BLOCKHASH
VALID, // 0x41 - COINBASE
VALID, // 0x42 - TIMESTAMP
VALID, // 0x43 - NUMBER
VALID, // 0x44 - DIFFICULTY
VALID, // 0x45 - GASLIMIT
VALID, // 0x46 - CHAINID
VALID, // 0x47 - SELFBALANCE
VALID, // 0x48 - BASEFEE
INVALID, // 0x49
INVALID, // 0x4a
INVALID, // 0x4b
INVALID, // 0x4c
INVALID, // 0x4d
INVALID, // 0x4e
INVALID, // 0x4f
VALID, // 0x50 - POP
VALID, // 0x51 - MLOAD
VALID, // 0x52 - MSTORE
VALID, // 0x53 - MSTORE8
VALID, // 0x54 - SLOAD
VALID, // 0x55 - SSTORE
VALID, // 0x56 - JUMP
VALID, // 0x57 - JUMPI
VALID, // 0x58 - PC
VALID, // 0x59 - MSIZE
VALID, // 0x5a - GAS
VALID_AND_JUMPDEST, // 0x5b - JUMPDEST
INVALID, // 0X5c
INVALID, // 0X5d
INVALID, // 0X5e
INVALID, // 0X5f - ?PUSH0?
VALID, // 0x60 - PUSH1
VALID, // 0x61 - PUSH2
VALID, // 0x62 - PUSH3
VALID, // 0x63 - PUSH4
VALID, // 0x64 - PUSH5
VALID, // 0x65 - PUSH6
VALID, // 0x66 - PUSH7
VALID, // 0x67 - PUSH8
VALID, // 0x68 - PUSH9
VALID, // 0x69 - PUSH10
VALID, // 0x6a - PUSH11
VALID, // 0x6b - PUSH12
VALID, // 0x6c - PUSH13
VALID, // 0x6d - PUSH14
VALID, // 0x6e - PUSH15
VALID, // 0x6f - PUSH16
VALID, // 0x70 - PUSH17
VALID, // 0x71 - PUSH18
VALID, // 0x72 - PUSH19
VALID, // 0x73 - PUSH20
VALID, // 0x74 - PUSH21
VALID, // 0x75 - PUSH22
VALID, // 0x76 - PUSH23
VALID, // 0x77 - PUSH24
VALID, // 0x78 - PUSH25
VALID, // 0x79 - PUSH26
VALID, // 0x7a - PUSH27
VALID, // 0x7b - PUSH28
VALID, // 0x7c - PUSH29
VALID, // 0x7d - PUSH30
VALID, // 0x7e - PUSH31
VALID, // 0x7f - PUSH32
VALID, // 0x80 - DUP1
VALID, // 0x81 - DUP2
VALID, // 0x82 - DUP3
VALID, // 0x83 - DUP4
VALID, // 0x84 - DUP5
VALID, // 0x85 - DUP6
VALID, // 0x86 - DUP7
VALID, // 0x87 - DUP8
VALID, // 0x88 - DUP9
VALID, // 0x89 - DUP10
VALID, // 0x8a - DUP11
VALID, // 0x8b - DUP12
VALID, // 0x8c - DUP13
VALID, // 0x8d - DUP14
VALID, // 0x8e - DUP15
VALID, // 0x8f - DUP16
VALID, // 0x90 - SWAP1
VALID, // 0x91 - SWAP2
VALID, // 0x92 - SWAP3
VALID, // 0x93 - SWAP4
VALID, // 0x94 - SWAP5
VALID, // 0x95 - SWAP6
VALID, // 0x96 - SWAP7
VALID, // 0x97 - SWAP8
VALID, // 0x98 - SWAP9
VALID, // 0x99 - SWAP10
VALID, // 0x9a - SWAP11
VALID, // 0x9b - SWAP12
VALID, // 0x9c - SWAP13
VALID, // 0x9d - SWAP14
VALID, // 0x9e - SWAP15
VALID, // 0x9f - SWAP16
VALID, // 0xa0 - LOG0
VALID, // 0xa1 - LOG1
VALID, // 0xa2 - LOG2
VALID, // 0xa3 - LOG3
VALID, // 0xa4 - LOG4
INVALID, // 0xa5
INVALID, // 0xa6
INVALID, // 0xa7
INVALID, // 0xa8
INVALID, // 0xa9
INVALID, // 0xaa
INVALID, // 0xab
INVALID, // 0xac
INVALID, // 0xad
INVALID, // 0xae
INVALID, // 0xaf
INVALID, // 0xb0
INVALID, // 0xb1
INVALID, // 0xb2
INVALID, // 0xb3
INVALID, // 0xb4
INVALID, // 0xb5
INVALID, // 0xb6
INVALID, // 0xb7
INVALID, // 0xb8
INVALID, // 0xb9
INVALID, // 0xba
INVALID, // 0xbb
INVALID, // 0xbc
INVALID, // 0xbd
INVALID, // 0xbe
INVALID, // 0xbf
INVALID, // 0xc0
INVALID, // 0xc1
INVALID, // 0xc2
INVALID, // 0xc3
INVALID, // 0xc4
INVALID, // 0xc5
INVALID, // 0xc6
INVALID, // 0xc7
INVALID, // 0xc8
INVALID, // 0xc9
INVALID, // 0xca
INVALID, // 0xcb
INVALID, // 0xcc
INVALID, // 0xcd
INVALID, // 0xce
INVALID, // 0xcf
INVALID, // 0xd0
INVALID, // 0xd1
INVALID, // 0xd2
INVALID, // 0xd3
INVALID, // 0xd4
INVALID, // 0xd5
INVALID, // 0xd6
INVALID, // 0xd7
INVALID, // 0xd8
INVALID, // 0xd9
INVALID, // 0xda
INVALID, // 0xdb
INVALID, // 0xdc
INVALID, // 0xdd
INVALID, // 0xde
INVALID, // 0xef
INVALID, // 0xe0
INVALID, // 0xe1
INVALID, // 0xe2
INVALID, // 0xe3
INVALID, // 0xe4
INVALID, // 0xe5
INVALID, // 0xe6
INVALID, // 0xe7
INVALID, // 0xe8
INVALID, // 0xe9
INVALID, // 0xea
INVALID, // 0xeb
INVALID, // 0xec
INVALID, // 0xed
INVALID, // 0xee
INVALID, // 0xef
VALID, // 0xf0 - CREATE
VALID, // 0xf1 - CALL
VALID, // 0xf2 - CALLCODE
VALID_AND_TERMINAL, // 0xf3 - RETURN
VALID, // 0xf4 - DELEGATECALL
VALID, // 0xf5 - CREATE2
INVALID, // 0xf6
INVALID, // 0xf7
INVALID, // 0xf8
INVALID, // 0xf9
VALID, // 0xfa - STATICCALL
INVALID, // 0xfb
INVALID, // 0xfc
VALID_AND_TERMINAL, // 0xfd - REVERT
VALID_AND_TERMINAL, // 0xfe - INVALID
VALID_AND_TERMINAL, // 0xff - SELFDESTRUCT
};
private OpcodesV1() {
// static utility class
}
static long[] validateAndCalculateJumpDests(final Bytes code) {
final int size = code.size();
final long[] bitmap = new long[(size >> 6) + 1];
final byte[] rawCode = code.toArrayUnsafe();
final int length = rawCode.length;
int attribute = INVALID;
for (int i = 0; i < length; ) {
long thisEntry = 0L;
final int entryPos = i >> 6;
final int max = Math.min(64, length - (entryPos << 6));
int j = i & 0x3f;
for (; j < max; i++, j++) {
final int operationNum = rawCode[i] & 0xff;
attribute = opcodeAttributes[operationNum];
if ((attribute & INVALID) == INVALID) {
return null;
} else if ((attribute & JUMPDEST) == JUMPDEST) {
thisEntry |= 1L << j;
} else if (operationNum > PushOperation.PUSH_BASE
&& operationNum <= PushOperation.PUSH_MAX) {
final int multiByteDataLen = operationNum - PushOperation.PUSH_BASE;
j += multiByteDataLen;
i += multiByteDataLen;
}
}
bitmap[entryPos] = thisEntry;
}
if ((attribute & TERMINAL) != TERMINAL) {
return null;
}
return bitmap;
}
}

@ -0,0 +1,38 @@
/*
* 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.contractvalidation;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
public class CachedInvalidCodeRule implements ContractValidationRule {
@Override
public Optional<ExceptionalHaltReason> validate(final MessageFrame frame) {
if (!frame.getCode().isValid()) {
return Optional.of(ExceptionalHaltReason.INVALID_CODE);
} else {
return Optional.empty();
}
}
public static ContractValidationRule of() {
return new CachedInvalidCodeRule();
}
}

@ -22,6 +22,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.contractvalidation.ContractValidationRule;
import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule;
import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule;
@ -59,7 +60,7 @@ public class EVMExecutor {
private Wei gasPriceGWei = Wei.ZERO;
private Bytes callData = Bytes.EMPTY;
private Wei ethValue = Wei.ZERO;
private Code code = Code.EMPTY_CODE;
private Code code = CodeV0.EMPTY_CODE;
private BlockValues blockValues = new SimpleBlockValues();
private OperationTracer tracer = OperationTracer.NO_TRACING;
private boolean requireDeposit = true;
@ -230,13 +231,10 @@ public class EVMExecutor {
while (!messageFrameStack.isEmpty()) {
final MessageFrame messageFrame = messageFrameStack.peek();
switch (messageFrame.getType()) {
case CONTRACT_CREATION:
ccp.process(messageFrame, tracer);
break;
case MESSAGE_CALL:
mcp.process(messageFrame, tracer);
break;
if (messageFrame.getType() == MessageFrame.Type.CONTRACT_CREATION) {
ccp.process(messageFrame, tracer);
} else if (messageFrame.getType() == MessageFrame.Type.MESSAGE_CALL) {
mcp.process(messageFrame, tracer);
}
}
if (commitWorldState) {

@ -31,6 +31,7 @@ public interface ExceptionalHaltReason {
ExceptionalHaltReason CODE_TOO_LARGE = DefaultExceptionalHaltReason.CODE_TOO_LARGE;
ExceptionalHaltReason INVALID_CODE = DefaultExceptionalHaltReason.INVALID_CODE;
ExceptionalHaltReason PRECOMPILE_ERROR = DefaultExceptionalHaltReason.PRECOMPILE_ERROR;
ExceptionalHaltReason INVALID_CODE_FORMAT = DefaultExceptionalHaltReason.INVALID_CODE_FORMAT;
String name();
@ -48,7 +49,8 @@ public interface ExceptionalHaltReason {
OUT_OF_BOUNDS("Out of bounds"),
CODE_TOO_LARGE("Code is too large"),
INVALID_CODE("Code is invalid"),
PRECOMPILE_ERROR("Precompile error");
PRECOMPILE_ERROR("Precompile error"),
INVALID_CODE_FORMAT("Code violates EOF validation rules");
final String description;

@ -21,6 +21,7 @@ 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;
@ -39,7 +40,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
protected static final OperationResult UNDERFLOW_RESPONSE =
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
protected AbstractCallOperation(
AbstractCallOperation(
final int opcode,
final String name,
final int stackItemsConsumed,
@ -182,7 +183,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
final Code code =
contract == null
? Code.EMPTY_CODE
? CodeV0.EMPTY_CODE
: evm.getCode(contract.getCodeHash(), contract.getCode());
final MessageFrame childFrame =

@ -19,8 +19,10 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
@ -69,7 +71,20 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
|| account.getNonce() == -1) {
fail(frame);
} else {
spawnChildMessage(frame, evm);
account.incrementNonce();
final long inputOffset = clampedToLong(frame.getStackItem(1));
final long inputSize = clampedToLong(frame.getStackItem(2));
final Bytes inputData = frame.readMemory(inputOffset, inputSize);
Code code = evm.getCode(Hash.hash(inputData), inputData);
if (code.isValid()) {
frame.decrementRemainingGas(cost);
spawnChildMessage(frame, code, evm);
frame.incrementRemainingGas(cost);
} else {
fail(frame);
}
}
return new OperationResult(cost, null);
@ -87,20 +102,8 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
frame.pushStackItem(UInt256.ZERO);
}
private void spawnChildMessage(final MessageFrame frame, final EVM evm) {
// memory cost needs to be calculated prior to memory expansion
final long cost = cost(frame);
frame.decrementRemainingGas(cost);
final Address address = frame.getRecipientAddress();
final MutableAccount account = frame.getWorldUpdater().getAccount(address).getMutable();
account.incrementNonce();
private void spawnChildMessage(final MessageFrame frame, final Code code, final EVM evm) {
final Wei value = Wei.wrap(frame.getStackItem(0));
final long inputOffset = clampedToLong(frame.getStackItem(1));
final long inputSize = clampedToLong(frame.getStackItem(2));
final Bytes inputData = frame.readMemory(inputOffset, inputSize);
final Address contractAddress = targetContractAddress(frame);
@ -122,34 +125,45 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
.sender(frame.getRecipientAddress())
.value(value)
.apparentValue(value)
.code(evm.getCode(Hash.hash(inputData), inputData))
.code(code)
.blockValues(frame.getBlockValues())
.depth(frame.getMessageStackDepth() + 1)
.completer(child -> complete(frame, child))
.completer(child -> complete(frame, child, evm))
.miningBeneficiary(frame.getMiningBeneficiary())
.blockHashLookup(frame.getBlockHashLookup())
.maxStackSize(frame.getMaxStackSize())
.build();
frame.incrementRemainingGas(cost);
frame.getMessageFrameStack().addFirst(childFrame);
frame.setState(MessageFrame.State.CODE_SUSPENDED);
}
private void complete(final MessageFrame frame, final MessageFrame childFrame) {
private void complete(final MessageFrame frame, final MessageFrame childFrame, final EVM evm) {
frame.setState(MessageFrame.State.CODE_EXECUTING);
frame.incrementRemainingGas(childFrame.getRemainingGas());
frame.addLogs(childFrame.getLogs());
frame.addSelfDestructs(childFrame.getSelfDestructs());
frame.incrementGasRefund(childFrame.getGasRefund());
Code outputCode =
CodeFactory.createCode(
childFrame.getOutputData(),
Hash.hash(childFrame.getOutputData()),
evm.getMaxEOFVersion(),
true);
frame.popStackItems(getStackItemsConsumed());
if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
frame.mergeWarmedUpFields(childFrame);
frame.pushStackItem(Words.fromAddress(childFrame.getContractAddress()));
if (outputCode.isValid()) {
frame.incrementRemainingGas(childFrame.getRemainingGas());
frame.addLogs(childFrame.getLogs());
frame.addSelfDestructs(childFrame.getSelfDestructs());
frame.incrementGasRefund(childFrame.getGasRefund());
if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
frame.mergeWarmedUpFields(childFrame);
frame.pushStackItem(Words.fromAddress(childFrame.getContractAddress()));
} else {
frame.setReturnData(childFrame.getOutputData());
frame.pushStackItem(UInt256.ZERO);
}
} else {
frame.getWorldUpdater().deleteAccount(childFrame.getRecipientAddress());
frame.setReturnData(childFrame.getOutputData());
frame.pushStackItem(UInt256.ZERO);
}

@ -41,7 +41,7 @@ public class CodeCopyOperation extends AbstractOperation {
final Code code = frame.getCode();
frame.writeMemory(memOffset, sourceOffset, numBytes, code.getBytes(), true);
frame.writeMemory(memOffset, sourceOffset, numBytes, code.getContainerBytes(), true);
return new OperationResult(cost, null);
}

@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.evm.operation;
import static org.hyperledger.besu.evm.operation.PushOperation.PUSH_BASE;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
@ -22,7 +24,6 @@ import org.apache.tuweni.bytes.Bytes;
public class Push0Operation extends AbstractFixedCostOperation {
public static final int PUSH_BASE = 0x5F;
static final OperationResult push0Success = new OperationResult(2, null);
public Push0Operation(final GasCalculator gasCalculator) {
@ -31,12 +32,11 @@ public class Push0Operation extends AbstractFixedCostOperation {
@Override
public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
return staticOperation(frame, frame.getPC());
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame, final int pc) {
public static OperationResult staticOperation(final MessageFrame frame) {
frame.pushStackItem(Bytes.EMPTY);
frame.setPC(pc + 1);
return push0Success;
}
}

@ -23,6 +23,7 @@ import org.apache.tuweni.bytes.Bytes;
public class PushOperation extends AbstractFixedCostOperation {
public static final int PUSH_BASE = 0x5F;
public static final int PUSH_MAX = 0x7F;
private final int length;
@ -41,9 +42,8 @@ public class PushOperation extends AbstractFixedCostOperation {
}
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
final byte[] code = frame.getCode().getBytes().toArrayUnsafe();
public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
final byte[] code = frame.getCode().getCodeBytes().toArrayUnsafe();
return staticOperation(frame, code, frame.getPC(), length);
}

@ -28,8 +28,8 @@ import org.hyperledger.besu.evm.tracing.OperationTracer;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -73,7 +73,7 @@ public class ContractCreationProcessor extends AbstractMessageProcessor {
requireCodeDepositToSucceed,
contractValidationRules,
initialContractNonce,
ImmutableSet.of());
Set.of());
}
private static boolean accountExists(final Account account) {

@ -40,6 +40,7 @@ public class StandardJsonTracer implements OperationTracer {
private List<String> stack;
private String gas;
private Bytes memory;
private int memorySize;
public StandardJsonTracer(final PrintStream out, final boolean showMemory) {
this.out = out;
@ -66,7 +67,10 @@ public class StandardJsonTracer implements OperationTracer {
}
pc = messageFrame.getPC();
gas = shortNumber(messageFrame.getRemainingGas());
memory = messageFrame.readMemory(0, messageFrame.memoryWordSize() * 32L);
memorySize = messageFrame.memoryWordSize() * 32;
if (showMemory) {
memory = messageFrame.readMemory(0, messageFrame.memoryWordSize() * 32L);
}
}
@Override
@ -85,15 +89,10 @@ public class StandardJsonTracer implements OperationTracer {
sb.append("\"gasCost\":\"").append(shortNumber(executeResult.getGasCost())).append("\",");
if (showMemory) {
sb.append("\"memory\":\"").append(memory.toHexString()).append("\",");
sb.append("\"memSize\":").append(memory.size()).append(",");
} else {
sb.append("\"memory\":\"0x\",");
sb.append("\"memSize\":").append(messageFrame.memoryByteSize()).append(",");
}
sb.append("\"memSize\":").append(memorySize).append(",");
sb.append("\"stack\":[").append(commaJoiner.join(stack)).append("],");
sb.append("\"returnData\":")
.append(returnData.size() > 0 ? '"' + returnData.toHexString() + '"' : "\"0x\"")
.append(",");
sb.append("\"returnData\":\"").append(returnData.toHexString()).append("\",");
sb.append("\"depth\":").append(depth).append(",");
sb.append("\"refund\":").append(messageFrame.getGasRefund()).append(",");
sb.append("\"opName\":\"").append(currentOp.getName()).append("\",");

@ -0,0 +1,113 @@
/*
* 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 static org.hyperledger.besu.evm.frame.MessageFrame.Type.MESSAGE_CALL;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.JumpDestOperation;
import org.hyperledger.besu.evm.operation.JumpOperation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.operation.OperationRegistry;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.ArrayDeque;
import javax.annotation.Nonnull;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class CodeV0Test {
private static final IstanbulGasCalculator gasCalculator = new IstanbulGasCalculator();
private static final int CURRENT_PC = 1;
private EVM evm;
@Before
public void startUp() {
final OperationRegistry registry = new OperationRegistry();
registry.put(new JumpOperation(gasCalculator));
registry.put(new JumpDestOperation(gasCalculator));
evm = new EVM(registry, gasCalculator, EvmConfiguration.DEFAULT, EvmSpecVersion.PARIS);
}
@Test
public void shouldReuseJumpDestMap() {
final JumpOperation operation = new JumpOperation(gasCalculator);
final Bytes jumpBytes = Bytes.fromHexString("0x6003565b00");
final CodeV0 getsCached =
(CodeV0) spy(CodeFactory.createCode(jumpBytes, Hash.hash(jumpBytes), 0, false));
MessageFrame frame = createJumpFrame(getsCached);
OperationResult result = operation.execute(frame, evm);
assertNull(result.getHaltReason());
Mockito.verify(getsCached, times(1)).calculateJumpDests();
// do it again to prove we don't recalculate, and we hit the cache
frame = createJumpFrame(getsCached);
result = operation.execute(frame, evm);
assertNull(result.getHaltReason());
Mockito.verify(getsCached, times(1)).calculateJumpDests();
}
@Nonnull
private MessageFrame createJumpFrame(final CodeV0 getsCached) {
final MessageFrame frame =
MessageFrame.builder()
.type(MESSAGE_CALL)
.messageFrameStack(new ArrayDeque<>())
.worldUpdater(mock(WorldUpdater.class))
.initialGas(10_000L)
.address(Address.ZERO)
.originator(Address.ZERO)
.contract(Address.ZERO)
.gasPrice(Wei.ZERO)
.inputData(Bytes.EMPTY)
.sender(Address.ZERO)
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(getsCached)
.blockValues(mock(BlockValues.class))
.depth(0)
.completer(f -> {})
.miningBeneficiary(Address.ZERO)
.blockHashLookup(l -> Hash.EMPTY)
.build();
frame.setPC(CURRENT_PC);
frame.pushStackItem(UInt256.fromHexString("0x03"));
return frame;
}
}

@ -0,0 +1,35 @@
/*
* 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 static org.assertj.core.api.Assertions.assertThat;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test;
class CodeV1Test {
@Test
void calculatesJumpDestMap() {
String codeHex = "0xEF000101000F006001600055600D5660026000555B00";
final EOFLayout layout = EOFLayout.parseEOF(Bytes.fromHexString(codeHex));
long[] jumpDest = OpcodesV1.validateAndCalculateJumpDests(layout.getSections()[1]);
assertThat(jumpDest).containsExactly(0x2000);
}
}

@ -0,0 +1,147 @@
/*
* 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 static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.Collection;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class EOFLayoutTest {
public static Collection<Object[]> testCasesFromEIP3540() {
return Arrays.asList(
new Object[][] {
{"EF", "No magic", "EOF Container too small", null},
{
"EFFF01010002020004006000AABBCCDD", "Invalid magic", "EOF header byte 1 incorrect", null
},
{"EF00", "No version", "EOF Container too small", null},
{
"EF0000010002020004006000AABBCCDD", "Invalid version", "Unsupported EOF Version 0", null
},
{
"EF0002010002020004006000AABBCCDD", "Invalid version", "Unsupported EOF Version 2", null
},
{
"EF00FF010002020004006000AABBCCDD",
"Invalid version",
"Unsupported EOF Version 255",
null
},
{"EF0001", "No header", "Improper section headers", null},
{"EF000100", "No code section", "Missing code (kind=1) section", null},
{"EF000101", "No code section size", "Improper section headers", null},
{"EF00010100", "Code section size incomplete", "Improper section headers", null},
{"EF0001010002", "No section terminator", "Improper section headers", null},
{
"EF000101000200", "No code section contents", "Missing or incomplete section data", null
},
{
"EF00010100020060",
"Code section contents incomplete",
"Missing or incomplete section data",
null
},
{
"EF0001010002006000DEADBEEF",
"Trailing bytes after code section",
"Dangling data at end of container",
null
},
{
"EF00010100020100020060006000",
"Multiple code sections",
"Duplicate section number 1",
null
},
{"EF000101000000", "Empty code section", "Empty section contents", null},
{"EF000101000002000200AABB", "Empty code section", "Empty section contents", null},
{
"EF000102000401000200AABBCCDD6000",
"Data section preceding code section",
"Code section cannot follow data section",
null
},
{
"EF000102000400AABBCCDD",
"Data section without code section",
"Missing code (kind=1) section",
null
},
{"EF000101000202", "No data section size", "Improper section headers", null},
{"EF00010100020200", "Data section size incomplete", "Improper section headers", null},
{"EF0001010002020004", "No section terminator", "Improper section headers", null},
{
"EF0001010002020004006000",
"No data section contents",
"Missing or incomplete section data",
null
},
{
"EF0001010002020004006000AABBCC",
"Data section contents incomplete",
"Missing or incomplete section data",
null
},
{
"EF0001010002020004006000AABBCCDDEE",
"Trailing bytes after data section",
"Dangling data at end of container",
null
},
{
"EF0001010002020004020004006000AABBCCDDAABBCCDD",
"Multiple data sections",
"Duplicate section number 2",
null
},
{"EF0001010002020000006000", "Empty data section", "Empty section contents", null},
{
"EF0001010002030004006000AABBCCDD",
"Unknown section (id = 3)",
"EOF Section kind 3 not supported",
null
},
{"EF0001010002006000", "Valid", null, "0x6000"},
});
}
@ParameterizedTest(name = "{1}")
@MethodSource("testCasesFromEIP3540")
void test(
final String containerString,
final String description,
final String failureReason,
final String code) {
final Bytes container = Bytes.fromHexString(containerString);
final EOFLayout layout = EOFLayout.parseEOF(container);
System.out.println(description);
assertThat(layout.getInvalidReason()).isEqualTo(failureReason);
if (code != null) {
assertThat(layout.getSections()).hasSize(3);
assertThat(layout.getSections()[1]).isNotNull();
assertThat(layout.getSections()[1].toHexString()).isEqualTo(code);
} else {
assertThat(layout.getSections()).isNull();
}
}
}

@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.operation.JumpDestOperation;
import org.apache.tuweni.bytes.Bytes;
@ -33,7 +34,8 @@ public class CodeCacheTest {
final Bytes contractBytes =
Bytes.fromHexString("0xDEAD" + op + "BEEF" + op + "B0B0" + op + "C0DE" + op + "FACE");
final CodeScale scale = new CodeScale();
final Code contractCode = Code.createLegacyCode(contractBytes, Hash.hash(contractBytes));
final Code contractCode =
CodeFactory.createCode(contractBytes, Hash.hash(contractBytes), 0, false);
final int weight = scale.weigh(contractCode.getCodeHash(), contractCode);
assertThat(weight)
.isEqualTo(contractCode.getCodeHash().size() + (contractBytes.size() * 9 + 7) / 8);

@ -28,7 +28,7 @@ import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator;
@ -65,7 +65,7 @@ public class Benchmarks {
.sender(Address.ZERO)
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(Code.EMPTY_CODE)
.code(CodeV0.EMPTY_CODE)
.depth(1)
.completer(__ -> {})
.address(Address.ZERO)
@ -79,7 +79,7 @@ public class Benchmarks {
.worldUpdater(mock(WorldUpdater.class))
.build();
public static void benchSecp256k1Recover() {
private static void benchSecp256k1Recover() {
final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance();
final SECPPrivateKey privateKey =
@ -135,6 +135,8 @@ public class Benchmarks {
}
private static void benchKeccak256() {
fakeFrame.expandMemory(0, 1024);
var istanbulGasCalculator = new IstanbulGasCalculator();
final byte[] warmupData = new byte[240];
final Bytes warmupBytes = Bytes.wrap(warmupData);
for (int i = 0; i < HASH_WARMUP; i++) {
@ -154,7 +156,9 @@ public class Benchmarks {
final double perCall = elapsed / HASH_ITERATIONS;
final double gasSpent = perCall * GAS_PER_SECOND_STANDARD;
System.out.printf("keccak256 %,d bytes for %,d gas.%n", len, (int) gasSpent);
System.out.printf(
"keccak256 %,d bytes for %,d gas. Charing %d gas.%n",
len, (int) gasSpent, istanbulGasCalculator.keccak256OperationGasCost(fakeFrame, 0, len));
}
}

Loading…
Cancel
Save