Migrate EVM execution loop into a switch (#4540)

Overview of changes:

* Remove vestigial log tracing. We will never use it in production and 
  standard json tracing gets us what we need
* Reduce use of lambdas and optionals. Reads great, translates into a 10% 
  perf hit in a tight loop.
* Unroll operation loop in some cases. Those are (a) ops that haven't 
  changed in any way since Frontier (b) ops not overridden in downstream 
  uses and (c) operations that translate into short static executions. 
  This has the longest tendrils as it is enabled by operations exposing 
  static methods to do their work.
* Refactoring of the operationTracer. The single, lambda consuming 
  traceExecution method was a barrier to performance. It has been replaced 
  with tracePreExecution and tracePostExecution. Look at 
  DebugOperationTracer to see how traces that need to operate on both 
  sides of the operation can be handled with this API.

Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
pull/4547/head
Danno Ferrin 2 years ago committed by GitHub
parent 8636a18492
commit 59d30448e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      CHANGELOG.md
  2. 35
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java
  3. 17
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java
  4. 26
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/EstimateGasOperationTracerTest.java
  5. 10
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/BaseFeeOperationTest.java
  6. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/ChainIdOperationTest.java
  7. 5
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/Create2OperationTest.java
  8. 6
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/ExtCodeHashOperationTest.java
  9. 14
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperationTest.java
  10. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/PrevRanDaoOperationTest.java
  11. 5
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperationTest.java
  12. 254
      evm/src/main/java/org/hyperledger/besu/evm/EVM.java
  13. 8
      evm/src/main/java/org/hyperledger/besu/evm/internal/FixedStack.java
  14. 13
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java
  15. 14
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java
  16. 16
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractFixedCostOperation.java
  17. 9
      evm/src/main/java/org/hyperledger/besu/evm/operation/AddModOperation.java
  18. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/AddOperation.java
  19. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/AndOperation.java
  20. 14
      evm/src/main/java/org/hyperledger/besu/evm/operation/BalanceOperation.java
  21. 4
      evm/src/main/java/org/hyperledger/besu/evm/operation/BaseFeeOperation.java
  22. 10
      evm/src/main/java/org/hyperledger/besu/evm/operation/ByteOperation.java
  23. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/CallDataCopyOperation.java
  24. 6
      evm/src/main/java/org/hyperledger/besu/evm/operation/CallOperation.java
  25. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/CodeCopyOperation.java
  26. 9
      evm/src/main/java/org/hyperledger/besu/evm/operation/DivOperation.java
  27. 17
      evm/src/main/java/org/hyperledger/besu/evm/operation/DupOperation.java
  28. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/EqOperation.java
  29. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/ExpOperation.java
  30. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java
  31. 14
      evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java
  32. 14
      evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java
  33. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/GtOperation.java
  34. 13
      evm/src/main/java/org/hyperledger/besu/evm/operation/InvalidOperation.java
  35. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/IsZeroOperation.java
  36. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/JumpOperation.java
  37. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/JumpiOperation.java
  38. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/Keccak256Operation.java
  39. 11
      evm/src/main/java/org/hyperledger/besu/evm/operation/LogOperation.java
  40. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/LtOperation.java
  41. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/MLoadOperation.java
  42. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/MStore8Operation.java
  43. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/MStoreOperation.java
  44. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/ModOperation.java
  45. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/MulModOperation.java
  46. 9
      evm/src/main/java/org/hyperledger/besu/evm/operation/MulOperation.java
  47. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/NotOperation.java
  48. 18
      evm/src/main/java/org/hyperledger/besu/evm/operation/Operation.java
  49. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/OrOperation.java
  50. 9
      evm/src/main/java/org/hyperledger/besu/evm/operation/PopOperation.java
  51. 30
      evm/src/main/java/org/hyperledger/besu/evm/operation/PushOperation.java
  52. 13
      evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataCopyOperation.java
  53. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java
  54. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java
  55. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/SDivOperation.java
  56. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/SGtOperation.java
  57. 26
      evm/src/main/java/org/hyperledger/besu/evm/operation/SLoadOperation.java
  58. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/SLtOperation.java
  59. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/SModOperation.java
  60. 18
      evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java
  61. 7
      evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperation.java
  62. 11
      evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java
  63. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperation.java
  64. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperation.java
  65. 34
      evm/src/main/java/org/hyperledger/besu/evm/operation/SignExtendOperation.java
  66. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java
  67. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/SubOperation.java
  68. 20
      evm/src/main/java/org/hyperledger/besu/evm/operation/SwapOperation.java
  69. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/XorOperation.java
  70. 20
      evm/src/main/java/org/hyperledger/besu/evm/tracing/EstimateGasOperationTracer.java
  71. 7
      evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java
  72. 43
      evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java
  73. 9
      evm/src/test/java/org/hyperledger/besu/evm/internal/OperandStackTest.java

@ -14,6 +14,9 @@
* For the EC encryptor, the encoded public key length is 91
- `--tx-pool-hashes-max-size` option removed (deprecated in 22.1.3)
- `--Xmerge-support` option remove (deprecated in 22.4.2) [#4518](https://github.com/hyperledger/besu/pull/4518)
- Breaking API changes in the `OperationTracer` interface to enable performance work.
* The `traceExecution` method has been replaced with `tracePreExecution` and `tracePostExecution` methods, called just before and just after operation execution.
* See `DebugOperationTracer` and `StandardJsonTracer` for migration examples.
### Additions and Improvements
- Reduce the number of runtime exceptions (SecurityModuleException) and unnecessary executions during ECIES handshake, by trying to decrypt EIP-8 formatted messages first [#4508](https://github.com/hyperledger/besu/pull/4508).
@ -25,6 +28,7 @@
- Continuously try to build better block proposals until timeout or GetPayload is called [#4516](https://github.com/hyperledger/besu/pull/4516)
- Upgrade RocksDB database version from 6.29.5 to 7.6.0 [#4517](https://github.com/hyperledger/besu/pull/4517)
- Avoid connecting to self when using static-nodes [#4521](https://github.com/hyperledger/besu/pull/4521)
- EVM performance has increased 20%-100% depending on the particulars of the contract. [#4540](https://github.com/hyperledger/besu/pull/4540)
### Bug Fixes
- Corrects emission of blockadded events when rewinding during a re-org. Fix for [#4495](https://github.com/hyperledger/besu/issues/4495)

@ -24,6 +24,7 @@ import org.hyperledger.besu.evm.ModificationNotAllowedException;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -44,25 +45,33 @@ public class DebugOperationTracer implements OperationTracer {
private List<TraceFrame> traceFrames = new ArrayList<>();
private TraceFrame lastFrame;
private Optional<Bytes32[]> preExecutionStack;
private long gasRemaining;
private Bytes inputData;
private int pc;
public DebugOperationTracer(final TraceOptions options) {
this.options = options;
}
@Override
public void traceExecution(final MessageFrame frame, final ExecuteOperation executeOperation) {
public void tracePreExecution(final MessageFrame frame) {
preExecutionStack = captureStack(frame);
gasRemaining = frame.getRemainingGas();
inputData = frame.getInputData().copy();
pc = frame.getPC();
}
@Override
public void tracePostExecution(final MessageFrame frame, final OperationResult operationResult) {
final Operation currentOperation = frame.getCurrentOperation();
final int depth = frame.getMessageStackDepth();
final String opcode = currentOperation.getName();
final int pc = frame.getPC();
final long gasRemaining = frame.getRemainingGas();
final Bytes inputData = frame.getInputData();
final Optional<Bytes32[]> stack = captureStack(frame);
final WorldUpdater worldUpdater = frame.getWorldUpdater();
final Optional<Bytes32[]> stackPostExecution;
final Operation.OperationResult operationResult = executeOperation.execute();
final Bytes outputData = frame.getOutputData();
final Optional<Bytes[]> memory = captureMemory(frame);
stackPostExecution = captureStack(frame);
final Optional<Bytes32[]> stackPostExecution = captureStack(frame);
if (lastFrame != null) {
lastFrame.setGasRemainingPostExecution(gasRemaining);
}
@ -74,15 +83,17 @@ public class DebugOperationTracer implements OperationTracer {
pc,
Optional.of(opcode),
gasRemaining,
operationResult.getGasCost(),
operationResult.getGasCost() == 0
? OptionalLong.empty()
: OptionalLong.of(operationResult.getGasCost()),
frame.getGasRefund(),
depth,
operationResult.getHaltReason(),
Optional.ofNullable(operationResult.getHaltReason()),
frame.getRecipientAddress(),
frame.getApparentValue(),
pc == 0 ? inputData.copy() : inputData,
inputData,
outputData,
stack,
preExecutionStack,
memory,
storage,
worldUpdater,

@ -36,8 +36,6 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.TreeMap;
import org.apache.tuweni.bytes.Bytes32;
@ -62,7 +60,7 @@ public class DebugOperationTracerTest {
new AbstractOperation(0x02, "MUL", 2, 1, 1, null) {
@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
return new OperationResult(OptionalLong.of(20L), Optional.empty());
return new OperationResult(20L, null);
}
};
@ -145,7 +143,7 @@ public class DebugOperationTracerTest {
final Map<UInt256, UInt256> updatedStorage = setupStorageForCapture(frame);
final TraceFrame traceFrame = traceFrame(frame, new TraceOptions(true, false, false));
assertThat(traceFrame.getStorage()).isPresent();
assertThat(traceFrame.getStorage().get()).isEqualTo(updatedStorage);
assertThat(traceFrame.getStorage()).contains(updatedStorage);
}
@Test
@ -162,11 +160,8 @@ public class DebugOperationTracerTest {
final DebugOperationTracer tracer =
new DebugOperationTracer(new TraceOptions(true, true, true));
tracer.traceExecution(
frame,
() ->
new OperationResult(
OptionalLong.of(50L), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS)));
tracer.tracePostExecution(
frame, new OperationResult(50L, ExceptionalHaltReason.INSUFFICIENT_GAS));
final TraceFrame traceFrame = getOnlyTraceFrame(tracer);
assertThat(traceFrame.getExceptionalHaltReason())
@ -180,7 +175,9 @@ public class DebugOperationTracerTest {
private TraceFrame traceFrame(final MessageFrame frame, final TraceOptions traceOptions) {
final DebugOperationTracer tracer = new DebugOperationTracer(traceOptions);
tracer.traceExecution(frame, () -> anOperation.execute(frame, null));
tracer.tracePreExecution(frame);
OperationResult operationResult = anOperation.execute(frame, null);
tracer.tracePostExecution(frame, operationResult);
return getOnlyTraceFrame(tracer);
}

@ -21,9 +21,9 @@ import org.hyperledger.besu.ethereum.core.MessageFrameTestFixture;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.operation.CallCodeOperation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.operation.SStoreOperation;
import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
import org.hyperledger.besu.evm.tracing.OperationTracer.ExecuteOperation;
import org.junit.Before;
import org.junit.Test;
@ -45,51 +45,51 @@ public class EstimateGasOperationTracerTest {
@Test
public void shouldDetectChangeInDepthDuringExecution() {
final ExecuteOperation noExecutionOperation = mock(ExecuteOperation.class);
final OperationResult testResult = new OperationResult(6, null);
assertThat(operationTracer.getMaxDepth()).isEqualTo(0);
assertThat(operationTracer.getMaxDepth()).isZero();
final MessageFrame firstFrame = messageFrameTestFixture.depth(0).build();
operationTracer.traceExecution(firstFrame, noExecutionOperation);
assertThat(operationTracer.getMaxDepth()).isEqualTo(0);
operationTracer.tracePostExecution(firstFrame, testResult);
assertThat(operationTracer.getMaxDepth()).isZero();
final MessageFrame secondFrame = messageFrameTestFixture.depth(1).build();
operationTracer.traceExecution(secondFrame, noExecutionOperation);
operationTracer.tracePostExecution(secondFrame, testResult);
assertThat(operationTracer.getMaxDepth()).isEqualTo(1);
final MessageFrame thirdFrame = messageFrameTestFixture.depth(1).build();
operationTracer.traceExecution(thirdFrame, noExecutionOperation);
operationTracer.tracePostExecution(thirdFrame, testResult);
assertThat(operationTracer.getMaxDepth()).isEqualTo(1);
final MessageFrame fourthFrame = messageFrameTestFixture.depth(2).build();
operationTracer.traceExecution(fourthFrame, noExecutionOperation);
operationTracer.tracePostExecution(fourthFrame, testResult);
assertThat(operationTracer.getMaxDepth()).isEqualTo(2);
final MessageFrame fifthFrame = messageFrameTestFixture.depth(0).build();
operationTracer.traceExecution(fifthFrame, noExecutionOperation);
operationTracer.tracePostExecution(fifthFrame, testResult);
assertThat(operationTracer.getMaxDepth()).isEqualTo(2);
}
@Test
public void shouldDetectMinimumGasRemainingForSStoreOperation() {
final ExecuteOperation noExecutionOperation = mock(ExecuteOperation.class);
final OperationResult testResult = new OperationResult(6, null);
final long minimumGasRemaining = 2300L;
assertThat(operationTracer.getStipendNeeded()).isZero();
final MessageFrame firstFrame = messageFrameTestFixture.build();
firstFrame.setCurrentOperation(mock(CallCodeOperation.class));
operationTracer.traceExecution(firstFrame, noExecutionOperation);
operationTracer.tracePostExecution(firstFrame, testResult);
assertThat(operationTracer.getStipendNeeded()).isZero();
final MessageFrame secondFrame = messageFrameTestFixture.build();
secondFrame.setCurrentOperation(
new SStoreOperation(mock(GasCalculator.class), minimumGasRemaining));
operationTracer.traceExecution(secondFrame, noExecutionOperation);
operationTracer.tracePostExecution(secondFrame, testResult);
assertThat(operationTracer.getStipendNeeded()).isEqualTo(minimumGasRemaining);
operationTracer.traceExecution(secondFrame, noExecutionOperation);
operationTracer.tracePostExecution(secondFrame, testResult);
assertThat(operationTracer.getStipendNeeded()).isEqualTo(minimumGasRemaining);
}
}

@ -15,7 +15,6 @@
package org.hyperledger.besu.ethereum.vm.operations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -45,8 +44,7 @@ public class BaseFeeOperationTest {
final MessageFrame frame = createMessageFrame(100, Optional.of(Wei.of(5L)));
final Operation operation = new BaseFeeOperation(gasCalculator);
final OperationResult result = operation.execute(frame, null);
assertThat(result.getGasCost().isPresent()).isTrue();
assertThat(result.getGasCost().getAsLong()).isEqualTo(gasCalculator.getBaseTierGasCost());
assertThat(result.getGasCost()).isEqualTo(gasCalculator.getBaseTierGasCost());
assertSuccessResult(result);
}
@ -55,7 +53,7 @@ public class BaseFeeOperationTest {
final MessageFrame frame = createMessageFrame(100, Optional.of(Wei.of(5L)));
final Operation operation = new BaseFeeOperation(gasCalculator);
final OperationResult result = operation.execute(frame, null);
verify(frame).pushStackItem(eq(UInt256.fromBytes(Bytes32.leftPad(Bytes.ofUnsignedLong(5L)))));
verify(frame).pushStackItem(UInt256.fromBytes(Bytes32.leftPad(Bytes.ofUnsignedLong(5L))));
assertSuccessResult(result);
}
@ -69,13 +67,13 @@ public class BaseFeeOperationTest {
private void assertSuccessResult(final OperationResult result) {
assertThat(result).isNotNull();
assertThat(result.getHaltReason()).isEmpty();
assertThat(result.getHaltReason()).isNull();
}
private void assertExceptionalHalt(
final OperationResult result, final ExceptionalHaltReason reason) {
assertThat(result).isNotNull();
assertThat(result.getHaltReason()).contains(reason);
assertThat(result.getHaltReason()).isEqualTo(reason);
}
private MessageFrame createMessageFrame(final long initialGas, final Optional<Wei> baseFee) {

@ -72,7 +72,6 @@ public class ChainIdOperationTest {
@Test
public void shouldCalculateGasPrice() {
final OperationResult result = operation.execute(messageFrame, null);
assertThat(result.getGasCost().isPresent()).isTrue();
assertThat(result.getGasCost().getAsLong()).isEqualTo(expectedGas);
assertThat(result.getGasCost()).isEqualTo(expectedGas);
}
}

@ -180,8 +180,7 @@ public class Create2OperationTest {
@Test
public void shouldCalculateGasPrice() {
final OperationResult result = operation.execute(messageFrame, evm);
assertThat(result.getHaltReason()).isEmpty();
assertThat(result.getGasCost().isPresent()).isTrue();
assertThat(result.getGasCost().getAsLong()).isEqualTo(expectedGas);
assertThat(result.getHaltReason()).isNull();
assertThat(result.getGasCost()).isEqualTo(expectedGas);
}
}

@ -58,16 +58,14 @@ public class ExtCodeHashOperationTest {
@Test
public void shouldCharge400Gas() {
final OperationResult result = operation.execute(createMessageFrame(REQUESTED_ADDRESS), null);
assertThat(result.getGasCost().isPresent()).isTrue();
assertThat(result.getGasCost().getAsLong()).isEqualTo(400L);
assertThat(result.getGasCost()).isEqualTo(400L);
}
@Test
public void istanbulShouldCharge700Gas() {
final OperationResult result =
operationIstanbul.execute(createMessageFrame(REQUESTED_ADDRESS), null);
assertThat(result.getGasCost().isPresent()).isTrue();
assertThat(result.getGasCost().getAsLong()).isEqualTo(700L);
assertThat(result.getGasCost()).isEqualTo(700L);
}
@Test

@ -100,7 +100,7 @@ public class JumpOperationTest {
frame.setPC(CURRENT_PC);
final OperationResult result = operation.execute(frame, evm);
assertThat(result.getHaltReason()).isEmpty();
assertThat(result.getHaltReason()).isNull();
}
@Test
@ -115,7 +115,7 @@ public class JumpOperationTest {
frame.setPC(CURRENT_PC);
final OperationResult result = operation.execute(frame, evm);
assertThat(result.getHaltReason()).isEmpty();
assertThat(result.getHaltReason()).isNull();
}
@Test
@ -130,7 +130,7 @@ public class JumpOperationTest {
frameDestinationGreaterThanCodeSize.setPC(CURRENT_PC);
final OperationResult result = operation.execute(frameDestinationGreaterThanCodeSize, evm);
assertThat(result.getHaltReason()).contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
assertThat(result.getHaltReason()).isEqualTo(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
final Bytes badJump = Bytes.fromHexString("0x60045600");
final MessageFrame frameDestinationEqualsToCodeSize =
createMessageFrameBuilder(100L)
@ -140,7 +140,7 @@ public class JumpOperationTest {
frameDestinationEqualsToCodeSize.setPC(CURRENT_PC);
final OperationResult result2 = operation.execute(frameDestinationEqualsToCodeSize, evm);
assertThat(result2.getHaltReason()).contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
assertThat(result2.getHaltReason()).isEqualTo(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
}
@Test
@ -158,7 +158,7 @@ public class JumpOperationTest {
longContract.setPC(255);
final OperationResult result = operation.execute(longContract, evm);
assertThat(result.getHaltReason()).isEmpty();
assertThat(result.getHaltReason()).isNull();
}
@Test
@ -174,7 +174,7 @@ public class JumpOperationTest {
frame.setPC(CURRENT_PC);
OperationResult result = operation.execute(frame, evm);
assertThat(result.getHaltReason()).isEmpty();
assertThat(result.getHaltReason()).isNull();
Mockito.verify(getsCached, times(1)).calculateJumpDests();
// do it again to prove we don't recalc, and we hit the cache
@ -187,7 +187,7 @@ public class JumpOperationTest {
frame.setPC(CURRENT_PC);
result = operation.execute(frame, evm);
assertThat(result.getHaltReason()).isEmpty();
assertThat(result.getHaltReason()).isNull();
Mockito.verify(getsCached, times(1)).calculateJumpDests();
}
}

@ -44,7 +44,7 @@ public class PrevRanDaoOperationTest {
when(messageFrame.getBlockValues()).thenReturn(blockHeader);
EVM evm = mock(EVM.class);
Operation.OperationResult r = op.executeFixedCostOperation(messageFrame, evm);
assertThat(r.getHaltReason()).isNotPresent();
assertThat(r.getHaltReason()).isNull();
verify(messageFrame).pushStackItem(prevRandao);
}
@ -60,7 +60,7 @@ public class PrevRanDaoOperationTest {
when(messageFrame.getBlockValues()).thenReturn(blockHeader);
EVM evm = mock(EVM.class);
Operation.OperationResult r = op.executeFixedCostOperation(messageFrame, evm);
assertThat(r.getHaltReason()).isNotPresent();
assertThat(r.getHaltReason()).isNull();
verify(messageFrame).pushStackItem(difficulty);
}
}

@ -35,7 +35,6 @@ import org.hyperledger.besu.evm.operation.SStoreOperation;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Arrays;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.Test;
@ -48,7 +47,7 @@ public class SStoreOperationTest {
private final long minimumGasAvailable;
private final long initialGas;
private final long remainingGas;
private final Optional<ExceptionalHaltReason> expectedHalt;
private final ExceptionalHaltReason expectedHalt;
private static final GasCalculator gasCalculator = new ConstantinopleGasCalculator();
@ -81,7 +80,7 @@ public class SStoreOperationTest {
this.minimumGasAvailable = minimumGasAvailable;
this.initialGas = initialGas;
this.remainingGas = remainingGas;
this.expectedHalt = Optional.ofNullable(expectedHalt);
this.expectedHalt = expectedHalt;
}
@Parameterized.Parameters(

@ -14,6 +14,9 @@
*/
package org.hyperledger.besu.evm;
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.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
@ -23,17 +26,35 @@ import org.hyperledger.besu.evm.internal.CodeCache;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.internal.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;
import org.hyperledger.besu.evm.operation.AddOperation;
import org.hyperledger.besu.evm.operation.AndOperation;
import org.hyperledger.besu.evm.operation.ByteOperation;
import org.hyperledger.besu.evm.operation.DupOperation;
import org.hyperledger.besu.evm.operation.InvalidOperation;
import org.hyperledger.besu.evm.operation.IsZeroOperation;
import org.hyperledger.besu.evm.operation.MulOperation;
import org.hyperledger.besu.evm.operation.NotOperation;
import org.hyperledger.besu.evm.operation.Operation;
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.PushOperation;
import org.hyperledger.besu.evm.operation.SGtOperation;
import org.hyperledger.besu.evm.operation.SLtOperation;
import org.hyperledger.besu.evm.operation.SModOperation;
import org.hyperledger.besu.evm.operation.SarOperation;
import org.hyperledger.besu.evm.operation.ShlOperation;
import org.hyperledger.besu.evm.operation.ShrOperation;
import org.hyperledger.besu.evm.operation.SignExtendOperation;
import org.hyperledger.besu.evm.operation.StopOperation;
import org.hyperledger.besu.evm.operation.SwapOperation;
import org.hyperledger.besu.evm.operation.VirtualOperation;
import org.hyperledger.besu.evm.operation.XorOperation;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes;
@ -44,11 +65,9 @@ public class EVM {
private static final Logger LOG = LoggerFactory.getLogger(EVM.class);
protected static final OperationResult OVERFLOW_RESPONSE =
new OperationResult(
OptionalLong.of(0L), Optional.of(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS));
new OperationResult(0L, ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
protected static final OperationResult UNDERFLOW_RESPONSE =
new OperationResult(
OptionalLong.of(0L), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
private final OperationRegistry operations;
private final GasCalculator gasCalculator;
@ -73,46 +92,213 @@ public class EVM {
// name of performance. This is one of the hottest sections of code.
//
// Please benchmark before refactoring.
public void runToHalt(final MessageFrame frame, final OperationTracer operationTracer) {
public void runToHalt(final MessageFrame frame, final OperationTracer tracing) {
var operationTracer = tracing == OperationTracer.NO_TRACING ? null : tracing;
byte[] code = frame.getCode().getBytes().toArrayUnsafe();
Operation[] operationArray = operations.getOperations();
while (frame.getState() == MessageFrame.State.CODE_EXECUTING) {
Operation currentOperation;
int opcode;
int pc = frame.getPC();
try {
int opcode = code[frame.getPC()] & 0xff;
opcode = code[pc] & 0xff;
currentOperation = operationArray[opcode];
} catch (ArrayIndexOutOfBoundsException aiiobe) {
opcode = 0;
currentOperation = endOfScriptStop;
}
frame.setCurrentOperation(currentOperation);
operationTracer.traceExecution(
frame,
() -> {
OperationResult result;
final Operation operation = frame.getCurrentOperation();
try {
result = operation.execute(frame, this);
} catch (final OverflowException oe) {
result = OVERFLOW_RESPONSE;
} catch (final UnderflowException ue) {
result = UNDERFLOW_RESPONSE;
}
final Optional<ExceptionalHaltReason> haltReason = result.getHaltReason();
if (haltReason.isPresent()) {
LOG.trace("MessageFrame evaluation halted because of {}", haltReason.get());
frame.setExceptionalHaltReason(haltReason);
frame.setState(State.EXCEPTIONAL_HALT);
} else if (result.getGasCost().isPresent()) {
frame.decrementRemainingGas(result.getGasCost().getAsLong());
}
if (frame.getState() == State.CODE_EXECUTING) {
final int currentPC = frame.getPC();
final int opSize = result.getPcIncrement();
frame.setPC(currentPC + opSize);
}
if (operationTracer != null) {
operationTracer.tracePreExecution(frame);
}
OperationResult result;
try {
return result;
});
switch (opcode) {
// case 0x00: // STOP
// result = StopOperation.staticOperation(frame);
// break;
case 0x01: // ADD
result = AddOperation.staticOperation(frame);
break;
case 0x02: // MUL
result = MulOperation.staticOperation(frame);
break;
// case 0x03: // SUB
// result = SubOperation.staticOperation(frame);
// break;
// case 0x04: // DIV
// result = DivOperation.staticOperation(frame);
// break;
// case 0x05: // SDIV
// result = SDivOperation.staticOperation(frame);
// break;
// case 0x06: // MOD
// result = ModOperation.staticOperation(frame);
// break;
case 0x07: // SMOD
result = SModOperation.staticOperation(frame);
break;
// case 0x08: // ADDMOD
// result = AddModOperation.staticOperation(frame);
// break;
// case 0x09: // MULMOD
// result = MulModOperation.staticOperation(frame);
// break;
// case 0x0a: //EXP requires gasCalculator access, so it is skipped
case 0x0b: // SIGNEXTEND
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;
// case 0x10: // LT
// result = LtOperation.staticOperation(frame);
// break;
// case 0x11: // GT
// result = GtOperation.staticOperation(frame);
// break;
case 0x12: // SLT
result = SLtOperation.staticOperation(frame);
break;
case 0x13: // SGT
result = SGtOperation.staticOperation(frame);
break;
case 0x15: // ISZERO
result = IsZeroOperation.staticOperation(frame);
break;
case 0x16: // AND
result = AndOperation.staticOperation(frame);
break;
case 0x17: // OR
result = OrOperation.staticOperation(frame);
break;
case 0x18: // XOR
result = XorOperation.staticOperation(frame);
break;
case 0x19: // NOT
result = NotOperation.staticOperation(frame);
break;
case 0x1a: // BYTE
result = ByteOperation.staticOperation(frame);
break;
case 0x1b: // SHL
result = ShlOperation.staticOperation(frame);
break;
case 0x1c: // SHR
result = ShrOperation.staticOperation(frame);
break;
case 0x1d: // SAR
result = SarOperation.staticOperation(frame);
break;
case 0x50: // POP
result = PopOperation.staticOperation(frame);
break;
case 0x60: // PUSH1-32
case 0x61:
case 0x62:
case 0x63:
case 0x64:
case 0x65:
case 0x66:
case 0x67:
case 0x68:
case 0x69:
case 0x6a:
case 0x6b:
case 0x6c:
case 0x6d:
case 0x6e:
case 0x6f:
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77:
case 0x78:
case 0x79:
case 0x7a:
case 0x7b:
case 0x7c:
case 0x7d:
case 0x7e:
case 0x7f:
result = PushOperation.staticOperation(frame, code, pc, opcode - PUSH_BASE);
break;
case 0x80: // DUP1-16
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f:
result = DupOperation.staticOperation(frame, opcode - DupOperation.DUP_BASE);
break;
case 0x90: // SWAP1-16
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
result = SwapOperation.staticOperation(frame, opcode - SWAP_BASE);
break;
default: // unoptimized operations
frame.setCurrentOperation(currentOperation);
result = currentOperation.execute(frame, this);
break;
}
} catch (final OverflowException oe) {
result = OVERFLOW_RESPONSE;
} catch (final UnderflowException ue) {
result = UNDERFLOW_RESPONSE;
}
if (operationTracer != null) {
operationTracer.tracePostExecution(frame, result);
}
final ExceptionalHaltReason haltReason = result.getHaltReason();
if (haltReason != null) {
LOG.trace("MessageFrame evaluation halted because of {}", haltReason);
frame.setExceptionalHaltReason(Optional.of(haltReason));
frame.setState(State.EXCEPTIONAL_HALT);
}
frame.decrementRemainingGas(result.getGasCost());
if (frame.getState() == State.CODE_EXECUTING) {
final int currentPC = frame.getPC();
final int opSize = result.getPcIncrement();
frame.setPC(currentPC + opSize);
}
}
}

@ -114,8 +114,10 @@ public class FixedStack<T> {
}
public void set(final int offset, final T operand) {
if (offset < 0 || offset >= size()) {
throw new IndexOutOfBoundsException();
if (offset < 0) {
throw new UnderflowException();
} else if (offset >= size()) {
throw new OverflowException();
}
entries[top - offset] = operand;
@ -129,7 +131,7 @@ public class FixedStack<T> {
public String toString() {
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < entries.length; ++i) {
builder.append(String.format("\n0x%04X ", i)).append(entries[i]);
builder.append(String.format("%n0x%04X ", i)).append(entries[i]);
}
return builder.toString();
}

@ -25,9 +25,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
@ -40,8 +37,7 @@ import org.apache.tuweni.units.bigints.UInt256;
public abstract class AbstractCallOperation extends AbstractOperation {
protected static final OperationResult UNDERFLOW_RESPONSE =
new OperationResult(
OptionalLong.of(0L), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
protected AbstractCallOperation(
final int opcode,
@ -160,8 +156,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
final long cost = cost(frame);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
frame.decrementRemainingGas(cost);
@ -180,7 +175,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
frame.incrementRemainingGas(gasAvailableForChildCall(frame) + cost);
frame.popStackItems(getStackItemsConsumed());
frame.pushStackItem(UInt256.ZERO);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
final Bytes inputData = frame.readMutableMemory(inputDataOffset(frame), inputDataLength(frame));
@ -217,7 +212,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
frame.getMessageFrameStack().addFirst(childFrame);
frame.setState(MessageFrame.State.CODE_SUSPENDED);
return new OperationResult(OptionalLong.of(cost), Optional.empty(), 0);
return new OperationResult(cost, null, 0);
}
protected abstract long cost(final MessageFrame frame);

@ -26,17 +26,13 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
public abstract class AbstractCreateOperation extends AbstractOperation {
protected static final OperationResult UNDERFLOW_RESPONSE =
new OperationResult(
OptionalLong.of(0L), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
protected AbstractCreateOperation(
final int opcode,
@ -57,11 +53,9 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
final long cost = cost(frame);
if (frame.isStatic()) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE));
return new OperationResult(cost, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
} else if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Wei value = Wei.wrap(frame.getStackItem(0));
@ -78,7 +72,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
spawnChildMessage(frame, evm);
}
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
protected abstract long cost(final MessageFrame frame);

@ -23,9 +23,6 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;
import java.util.Optional;
import java.util.OptionalLong;
abstract class AbstractFixedCostOperation extends AbstractOperation {
protected final OperationResult successResponse;
@ -44,16 +41,11 @@ abstract class AbstractFixedCostOperation extends AbstractOperation {
final long fixedCost) {
super(opcode, name, stackItemsConsumed, stackItemsProduced, opSize, gasCalculator);
gasCost = fixedCost;
successResponse = new OperationResult(OptionalLong.of(gasCost), Optional.empty());
outOfGasResponse =
new OperationResult(
OptionalLong.of(gasCost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
successResponse = new OperationResult(gasCost, null);
outOfGasResponse = new OperationResult(gasCost, ExceptionalHaltReason.INSUFFICIENT_GAS);
underflowResponse =
new OperationResult(
OptionalLong.of(gasCost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
overflowResponse =
new OperationResult(
OptionalLong.of(gasCost), Optional.of(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS));
new OperationResult(gasCost, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
overflowResponse = new OperationResult(gasCost, ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
@Override

@ -26,6 +26,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class AddModOperation extends AbstractFixedCostOperation {
private static final OperationResult addModSuccess = new OperationResult(8, null);
public AddModOperation(final GasCalculator gasCalculator) {
super(0x08, "ADDMOD", 3, 1, 1, gasCalculator, gasCalculator.getMidTierGasCost());
}
@ -33,6 +35,11 @@ public class AddModOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final Bytes value0 = frame.popStackItem();
final Bytes value1 = frame.popStackItem();
final Bytes value2 = frame.popStackItem();
@ -55,6 +62,6 @@ public class AddModOperation extends AbstractFixedCostOperation {
frame.pushStackItem(Bytes.concatenate(Bytes.wrap(padding), resultBytes));
}
return successResponse;
return addModSuccess;
}
}

@ -24,6 +24,8 @@ import org.apache.tuweni.bytes.Bytes;
public class AddOperation extends AbstractFixedCostOperation {
static final OperationResult addSuccess = new OperationResult(3, null);
public AddOperation(final GasCalculator gasCalculator) {
super(0x01, "ADD", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -31,6 +33,10 @@ public class AddOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final BigInteger value0 = new BigInteger(1, frame.popStackItem().toArrayUnsafe());
final BigInteger value1 = new BigInteger(1, frame.popStackItem().toArrayUnsafe());
@ -44,6 +50,6 @@ public class AddOperation extends AbstractFixedCostOperation {
frame.pushStackItem(Bytes.wrap(resultArray));
}
return successResponse;
return addSuccess;
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class AndOperation extends AbstractFixedCostOperation {
static final OperationResult andSuccess = new OperationResult(3, null);
public AndOperation(final GasCalculator gasCalculator) {
super(0x16, "AND", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,12 +31,16 @@ public class AndOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
final UInt256 result = value0.and(value1);
frame.pushStackItem(result);
return successResponse;
return andSuccess;
}
}

@ -24,9 +24,6 @@ import org.hyperledger.besu.evm.internal.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.units.bigints.UInt256;
public class BalanceOperation extends AbstractOperation {
@ -50,19 +47,16 @@ public class BalanceOperation extends AbstractOperation {
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(accountIsWarm);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else {
final Account account = frame.getWorldUpdater().get(address);
frame.pushStackItem(account == null ? UInt256.ZERO : account.getBalance());
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
} catch (final UnderflowException ufe) {
return new OperationResult(
OptionalLong.of(cost(true)), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
return new OperationResult(cost(true), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
} catch (final OverflowException ofe) {
return new OperationResult(
OptionalLong.of(cost(true)), Optional.of(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS));
return new OperationResult(cost(true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
}
}

@ -21,7 +21,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
public class BaseFeeOperation extends AbstractFixedCostOperation {
@ -34,8 +33,7 @@ public class BaseFeeOperation extends AbstractFixedCostOperation {
final MessageFrame frame, final EVM evm) {
final Optional<Wei> maybeBaseFee = frame.getBlockValues().getBaseFee();
if (maybeBaseFee.isEmpty()) {
return new Operation.OperationResult(
OptionalLong.of(gasCost), Optional.of(ExceptionalHaltReason.INVALID_OPERATION));
return new Operation.OperationResult(gasCost, ExceptionalHaltReason.INVALID_OPERATION);
}
frame.pushStackItem(maybeBaseFee.orElseThrow());
return successResponse;

@ -23,11 +23,13 @@ import org.apache.tuweni.units.bigints.UInt256;
public class ByteOperation extends AbstractFixedCostOperation {
static final OperationResult byteSuccess = new OperationResult(3, null);
public ByteOperation(final GasCalculator gasCalculator) {
super(0x1A, "BYTE", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
private UInt256 getByte(final UInt256 seq, final UInt256 offset) {
private static UInt256 getByte(final UInt256 seq, final UInt256 offset) {
if (!offset.fitsInt()) {
return UInt256.ZERO;
}
@ -46,6 +48,10 @@ public class ByteOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
@ -54,6 +60,6 @@ public class ByteOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return byteSuccess;
}
}

@ -21,9 +21,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class CallDataCopyOperation extends AbstractOperation {
@ -40,14 +37,13 @@ public class CallDataCopyOperation extends AbstractOperation {
final long cost = gasCalculator().dataCopyOperationGasCost(frame, memOffset, numBytes);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Bytes callData = frame.getInputData();
frame.writeMemory(memOffset, sourceOffset, numBytes, callData, true);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -25,9 +25,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import java.util.OptionalLong;
public class CallOperation extends AbstractCallOperation {
public CallOperation(final GasCalculator gasCalculator) {
@ -114,8 +111,7 @@ public class CallOperation extends AbstractCallOperation {
@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
if (frame.isStatic() && !value(frame).isZero()) {
return new OperationResult(
OptionalLong.of(cost(frame)), Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE));
return new OperationResult(cost(frame), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
} else {
return super.execute(frame, evm);
}

@ -22,9 +22,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
public class CodeCopyOperation extends AbstractOperation {
public CodeCopyOperation(final GasCalculator gasCalculator) {
@ -39,14 +36,13 @@ public class CodeCopyOperation extends AbstractOperation {
final long cost = gasCalculator().dataCopyOperationGasCost(frame, memOffset, numBytes);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Code code = frame.getCode();
frame.writeMemory(memOffset, sourceOffset, numBytes, code.getBytes(), true);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -25,6 +25,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class DivOperation extends AbstractFixedCostOperation {
static final OperationResult divSuccess = new OperationResult(5, null);
public DivOperation(final GasCalculator gasCalculator) {
super(0x04, "DIV", 2, 1, 1, gasCalculator, gasCalculator.getLowTierGasCost());
}
@ -32,6 +34,11 @@ public class DivOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final Bytes value0 = frame.popStackItem();
final Bytes value1 = frame.popStackItem();
@ -53,6 +60,6 @@ public class DivOperation extends AbstractFixedCostOperation {
}
}
return successResponse;
return divSuccess;
}
}

@ -19,11 +19,10 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
public class DupOperation extends AbstractFixedCostOperation {
public static final int DUP_BASE = 0x7F;
static final OperationResult dupSuccess = new OperationResult(3, null);
protected final Operation.OperationResult underflowResponse;
private final int index;
@ -39,20 +38,18 @@ public class DupOperation extends AbstractFixedCostOperation {
gasCalculator.getVeryLowTierGasCost());
this.index = index;
this.underflowResponse =
new Operation.OperationResult(
OptionalLong.of(gasCost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
new Operation.OperationResult(gasCost, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
}
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
// getStackItem won't throw under/overflows. Check explicitly.
if (frame.stackSize() < getStackItemsConsumed()) {
return underflowResponse;
}
return staticOperation(frame, index);
}
public static OperationResult staticOperation(final MessageFrame frame, final int index) {
frame.pushStackItem(frame.getStackItem(index - 1));
return successResponse;
return dupSuccess;
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class EqOperation extends AbstractFixedCostOperation {
static final OperationResult eqSuccess = new OperationResult(3, null);
public EqOperation(final GasCalculator gasCalculator) {
super(0x14, "EQ", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,6 +31,10 @@ public class EqOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
@ -36,6 +42,6 @@ public class EqOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return eqSuccess;
}
}

@ -19,9 +19,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.units.bigints.UInt256;
public class ExpOperation extends AbstractOperation {
@ -39,13 +36,12 @@ public class ExpOperation extends AbstractOperation {
final long cost = gasCalculator().expOperationGasCost(numBytes);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final UInt256 result = number.pow(power);
frame.pushStackItem(result);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -25,9 +25,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class ExtCodeCopyOperation extends AbstractOperation {
@ -60,14 +57,13 @@ public class ExtCodeCopyOperation extends AbstractOperation {
final long cost = cost(frame, memOffset, numBytes, accountIsWarm);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Account account = frame.getWorldUpdater().get(address);
final Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
frame.writeMemory(memOffset, sourceOffset, numBytes, code);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -24,9 +24,6 @@ import org.hyperledger.besu.evm.internal.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.units.bigints.UInt256;
public class ExtCodeHashOperation extends AbstractOperation {
@ -50,8 +47,7 @@ public class ExtCodeHashOperation extends AbstractOperation {
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(accountIsWarm);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else {
final Account account = frame.getWorldUpdater().get(address);
if (account == null || account.isEmpty()) {
@ -59,14 +55,12 @@ public class ExtCodeHashOperation extends AbstractOperation {
} else {
frame.pushStackItem(UInt256.fromBytes(account.getCodeHash()));
}
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
} catch (final UnderflowException ufe) {
return new OperationResult(
OptionalLong.of(cost(true)), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
return new OperationResult(cost(true), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
} catch (final OverflowException ofe) {
return new OperationResult(
OptionalLong.of(cost(true)), Optional.of(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS));
return new OperationResult(cost(true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
}
}

@ -24,9 +24,6 @@ import org.hyperledger.besu.evm.internal.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.units.bigints.UInt256;
public class ExtCodeSizeOperation extends AbstractOperation {
@ -50,20 +47,17 @@ public class ExtCodeSizeOperation extends AbstractOperation {
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(accountIsWarm);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else {
final Account account = frame.getWorldUpdater().get(address);
frame.pushStackItem(
account == null ? UInt256.ZERO : UInt256.valueOf(account.getCode().size()));
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
} catch (final UnderflowException ufe) {
return new OperationResult(
OptionalLong.of(cost(true)), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
return new OperationResult(cost(true), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
} catch (final OverflowException ofe) {
return new OperationResult(
OptionalLong.of(cost(true)), Optional.of(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS));
return new OperationResult(cost(true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class GtOperation extends AbstractFixedCostOperation {
static final OperationResult gtSuccess = new OperationResult(3, null);
public GtOperation(final GasCalculator gasCalculator) {
super(0x11, "GT", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,6 +31,10 @@ public class GtOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
@ -36,6 +42,6 @@ public class GtOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return gtSuccess;
}
}

@ -19,13 +19,12 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
public class InvalidOperation extends AbstractOperation {
public static final int OPCODE = 0xFE;
protected final OperationResult invalidOperation;
public static final OperationResult INVALID_RESULT =
new OperationResult(0, ExceptionalHaltReason.INVALID_OPERATION);
protected final OperationResult invalidResult;
public InvalidOperation(final GasCalculator gasCalculator) {
this(OPCODE, gasCalculator);
@ -33,13 +32,11 @@ public class InvalidOperation extends AbstractOperation {
public InvalidOperation(final int opcode, final GasCalculator gasCalculator) {
super(opcode, "INVALID", -1, -1, 1, gasCalculator);
invalidOperation =
new OperationResult(
OptionalLong.of(0L), Optional.of(ExceptionalHaltReason.INVALID_OPERATION));
invalidResult = new OperationResult(0L, ExceptionalHaltReason.INVALID_OPERATION);
}
@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
return invalidOperation;
return invalidResult;
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class IsZeroOperation extends AbstractFixedCostOperation {
static final OperationResult isZeroSuccess = new OperationResult(3, null);
public IsZeroOperation(final GasCalculator gasCalculator) {
super(0x15, "ISZERO", 1, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,10 +31,14 @@ public class IsZeroOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value = UInt256.fromBytes(frame.popStackItem());
frame.pushStackItem(value.isZero() ? UInt256.ONE : UInt256.ZERO);
return successResponse;
return isZeroSuccess;
}
}

@ -20,9 +20,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class JumpOperation extends AbstractFixedCostOperation {
@ -33,9 +30,8 @@ public class JumpOperation extends AbstractFixedCostOperation {
public JumpOperation(final GasCalculator gasCalculator) {
super(0x56, "JUMP", 2, 0, 1, gasCalculator, gasCalculator.getMidTierGasCost());
invalidJumpResponse =
new Operation.OperationResult(
OptionalLong.of(gasCost), Optional.of(ExceptionalHaltReason.INVALID_JUMP_DESTINATION));
jumpResponse = new OperationResult(OptionalLong.of(gasCost), Optional.empty(), 0);
new Operation.OperationResult(gasCost, ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
jumpResponse = new OperationResult(gasCost, null, 0);
}
@Override

@ -20,9 +20,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class JumpiOperation extends AbstractFixedCostOperation {
@ -33,9 +30,8 @@ public class JumpiOperation extends AbstractFixedCostOperation {
public JumpiOperation(final GasCalculator gasCalculator) {
super(0x57, "JUMPI", 2, 0, 1, gasCalculator, gasCalculator.getHighTierGasCost());
invalidJumpResponse =
new Operation.OperationResult(
OptionalLong.of(gasCost), Optional.of(ExceptionalHaltReason.INVALID_JUMP_DESTINATION));
jumpResponse = new OperationResult(OptionalLong.of(gasCost), Optional.empty(), 0);
new Operation.OperationResult(gasCost, ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
jumpResponse = new OperationResult(gasCost, null, 0);
}
@Override

@ -22,9 +22,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
@ -41,12 +38,11 @@ public class Keccak256Operation extends AbstractOperation {
final long cost = gasCalculator().keccak256OperationGasCost(frame, from, length);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Bytes bytes = frame.readMutableMemory(from, length);
frame.pushStackItem(UInt256.fromBytes(keccak256(bytes)));
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -25,9 +25,6 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.log.LogTopic;
import java.util.Optional;
import java.util.OptionalLong;
import com.google.common.collect.ImmutableList;
import org.apache.tuweni.bytes.Bytes;
@ -47,11 +44,9 @@ public class LogOperation extends AbstractOperation {
final long cost = gasCalculator().logOperationGasCost(frame, dataLocation, numBytes, numTopics);
if (frame.isStatic()) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE));
return new OperationResult(cost, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
} else if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Address address = frame.getRecipientAddress();
@ -65,6 +60,6 @@ public class LogOperation extends AbstractOperation {
}
frame.addLog(new Log(address, data, builder.build()));
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class LtOperation extends AbstractFixedCostOperation {
static final OperationResult ltSuccess = new OperationResult(3, null);
public LtOperation(final GasCalculator gasCalculator) {
super(0x10, "LT", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,6 +31,10 @@ public class LtOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
@ -36,6 +42,6 @@ public class LtOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return ltSuccess;
}
}

@ -21,9 +21,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class MLoadOperation extends AbstractOperation {
@ -38,13 +35,12 @@ public class MLoadOperation extends AbstractOperation {
final long cost = gasCalculator().mLoadOperationGasCost(frame, location);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Bytes value = frame.readMutableMemory(location, 32, true).copy();
frame.pushStackItem(value);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -21,9 +21,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class MStore8Operation extends AbstractOperation {
@ -40,11 +37,10 @@ public class MStore8Operation extends AbstractOperation {
final long cost = gasCalculator().mStore8OperationGasCost(frame, location);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
frame.writeMemory(location, theByte, true);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -21,9 +21,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class MStoreOperation extends AbstractOperation {
@ -39,11 +36,10 @@ public class MStoreOperation extends AbstractOperation {
final long cost = gasCalculator().mStoreOperationGasCost(frame, location);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
frame.writeMemoryRightAligned(location, 32, value, true);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -26,6 +26,8 @@ import org.apache.tuweni.bytes.Bytes32;
public class ModOperation extends AbstractFixedCostOperation {
private static final OperationResult modSuccess = new OperationResult(5, null);
public ModOperation(final GasCalculator gasCalculator) {
super(0x06, "MOD", 2, 1, 1, gasCalculator, gasCalculator.getLowTierGasCost());
}
@ -33,6 +35,10 @@ public class ModOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final Bytes value0 = frame.popStackItem();
final Bytes value1 = frame.popStackItem();
if (value1.isZero()) {
@ -53,6 +59,6 @@ public class ModOperation extends AbstractFixedCostOperation {
frame.pushStackItem(Bytes.concatenate(Bytes.wrap(padding), resultBytes));
}
return successResponse;
return modSuccess;
}
}

@ -26,6 +26,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class MulModOperation extends AbstractFixedCostOperation {
private static final OperationResult mulModSuccess = new OperationResult(8, null);
public MulModOperation(final GasCalculator gasCalculator) {
super(0x09, "MULMOD", 3, 1, 1, gasCalculator, gasCalculator.getMidTierGasCost());
}
@ -33,6 +35,10 @@ public class MulModOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final Bytes value0 = frame.popStackItem();
final Bytes value1 = frame.popStackItem();
final Bytes value2 = frame.popStackItem();
@ -56,6 +62,6 @@ public class MulModOperation extends AbstractFixedCostOperation {
frame.pushStackItem(Bytes.concatenate(Bytes.wrap(padding), resultBytes));
}
return successResponse;
return mulModSuccess;
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class MulOperation extends AbstractFixedCostOperation {
static final OperationResult mulSuccess = new OperationResult(5, null);
public MulOperation(final GasCalculator gasCalculator) {
super(0x02, "MUL", 2, 1, 1, gasCalculator, gasCalculator.getLowTierGasCost());
}
@ -29,6 +31,11 @@ public class MulOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
@ -36,6 +43,6 @@ public class MulOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return mulSuccess;
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class NotOperation extends AbstractFixedCostOperation {
static final OperationResult notSuccess = new OperationResult(3, null);
public NotOperation(final GasCalculator gasCalculator) {
super(0x19, "NOT", 1, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,12 +31,16 @@ public class NotOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value = UInt256.fromBytes(frame.popStackItem());
final UInt256 result = value.not();
frame.pushStackItem(result);
return successResponse;
return notSuccess;
}
}

@ -18,35 +18,29 @@ import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
import java.util.OptionalLong;
public interface Operation {
class OperationResult {
final OptionalLong gasCost;
final Optional<ExceptionalHaltReason> haltReason;
final long gasCost;
final ExceptionalHaltReason haltReason;
final int pcIncrement;
public OperationResult(
final OptionalLong gasCost, final Optional<ExceptionalHaltReason> haltReason) {
public OperationResult(final long gasCost, final ExceptionalHaltReason haltReason) {
this(gasCost, haltReason, 1);
}
public OperationResult(
final OptionalLong gasCost,
final Optional<ExceptionalHaltReason> haltReason,
final int pcIncrement) {
final long gasCost, final ExceptionalHaltReason haltReason, final int pcIncrement) {
this.gasCost = gasCost;
this.haltReason = haltReason;
this.pcIncrement = pcIncrement;
}
public OptionalLong getGasCost() {
public long getGasCost() {
return gasCost;
}
public Optional<ExceptionalHaltReason> getHaltReason() {
public ExceptionalHaltReason getHaltReason() {
return haltReason;
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class OrOperation extends AbstractFixedCostOperation {
static final OperationResult orSuccess = new OperationResult(3, null);
public OrOperation(final GasCalculator gasCalculator) {
super(0x17, "OR", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,6 +31,10 @@ public class OrOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
@ -36,6 +42,6 @@ public class OrOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return orSuccess;
}
}

@ -20,6 +20,8 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
public class PopOperation extends AbstractFixedCostOperation {
static final OperationResult popSuccess = new OperationResult(2, null);
public PopOperation(final GasCalculator gasCalculator) {
super(0x50, "POP", 1, 0, 1, gasCalculator, gasCalculator.getBaseTierGasCost());
}
@ -27,8 +29,11 @@ public class PopOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
frame.popStackItem();
return staticOperation(frame);
}
return successResponse;
public static OperationResult staticOperation(final MessageFrame frame) {
frame.popStackItem();
return popSuccess;
}
}

@ -14,15 +14,10 @@
*/
package org.hyperledger.besu.evm.operation;
import static java.lang.Math.min;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class PushOperation extends AbstractFixedCostOperation {
@ -31,7 +26,7 @@ public class PushOperation extends AbstractFixedCostOperation {
private final int length;
private final OperationResult pushResponse;
static final OperationResult pushSuccess = new OperationResult(3, null);
public PushOperation(final int length, final GasCalculator gasCalculator) {
super(
@ -43,18 +38,27 @@ public class PushOperation extends AbstractFixedCostOperation {
gasCalculator,
gasCalculator.getVeryLowTierGasCost());
this.length = length;
pushResponse = new OperationResult(OptionalLong.of(gasCost), Optional.empty(), length + 1);
}
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
final int pc = frame.getPC();
final Bytes code = frame.getCode().getBytes();
final int copyLength = min(length, code.size() - pc - 1);
frame.pushStackItem(code.slice(pc + 1, copyLength));
final byte[] code = frame.getCode().getBytes().toArrayUnsafe();
return staticOperation(frame, code, frame.getPC(), length);
}
return pushResponse;
public static OperationResult staticOperation(
final MessageFrame frame, final byte[] code, final int pc, final int pushSize) {
int copyStart = pc + 1;
Bytes push;
if (code.length <= copyStart) {
push = Bytes.EMPTY;
} else {
final int copyLength = Math.min(pushSize, code.length - pc - 1);
push = Bytes.wrap(code, copyStart, copyLength);
}
frame.pushStackItem(push);
frame.setPC(pc + pushSize);
return pushSuccess;
}
}

@ -21,18 +21,14 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class ReturnDataCopyOperation extends AbstractOperation {
protected static final OperationResult INVALID_RETURN_DATA_BUFFER_ACCESS =
new OperationResult(
OptionalLong.of(0), Optional.of(ExceptionalHaltReason.INVALID_RETURN_DATA_BUFFER_ACCESS));
new OperationResult(0L, ExceptionalHaltReason.INVALID_RETURN_DATA_BUFFER_ACCESS);
protected static final OperationResult OUT_OF_BOUNDS =
new OperationResult(OptionalLong.of(0L), Optional.of(ExceptionalHaltReason.OUT_OF_BOUNDS));
new OperationResult(0L, ExceptionalHaltReason.OUT_OF_BOUNDS);
public ReturnDataCopyOperation(final GasCalculator gasCalculator) {
super(0x3E, "RETURNDATACOPY", 3, 0, 1, gasCalculator);
@ -57,12 +53,11 @@ public class ReturnDataCopyOperation extends AbstractOperation {
final long cost = gasCalculator().dataCopyOperationGasCost(frame, memOffset, numBytes);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
frame.writeMemory(memOffset, sourceOffset, numBytes, returnData, true);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -21,9 +21,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
public class ReturnOperation extends AbstractOperation {
public ReturnOperation(final GasCalculator gasCalculator) {
@ -37,12 +34,11 @@ public class ReturnOperation extends AbstractOperation {
final long cost = gasCalculator().memoryExpansionGasCost(frame, from, length);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
frame.setOutputData(frame.readMemory(from, length));
frame.setState(MessageFrame.State.CODE_SUCCESS);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -21,9 +21,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class RevertOperation extends AbstractOperation {
@ -39,14 +36,13 @@ public class RevertOperation extends AbstractOperation {
final long cost = gasCalculator().memoryExpansionGasCost(frame, from, length);
if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Bytes reason = frame.readMemory(from, length);
frame.setOutputData(reason);
frame.setRevertReason(reason);
frame.setState(MessageFrame.State.REVERT);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -26,6 +26,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class SDivOperation extends AbstractFixedCostOperation {
private static final OperationResult sdivSuccess = new OperationResult(5, null);
public SDivOperation(final GasCalculator gasCalculator) {
super(0x05, "SDIV", 2, 1, 1, gasCalculator, gasCalculator.getLowTierGasCost());
}
@ -33,6 +35,10 @@ public class SDivOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final Bytes value0 = frame.popStackItem();
final Bytes value1 = frame.popStackItem();
@ -59,6 +65,6 @@ public class SDivOperation extends AbstractFixedCostOperation {
frame.pushStackItem(UInt256.fromBytes(Bytes.concatenate(Bytes.wrap(padding), resultBytes)));
}
return successResponse;
return sdivSuccess;
}
}

@ -25,6 +25,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class SGtOperation extends AbstractFixedCostOperation {
static final OperationResult sgtSuccess = new OperationResult(3, null);
public SGtOperation(final GasCalculator gasCalculator) {
super(0x13, "SGT", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -32,6 +34,10 @@ public class SGtOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final Bytes value0 = frame.popStackItem();
final Bytes value1 = frame.popStackItem();
@ -48,6 +54,6 @@ public class SGtOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return sgtSuccess;
}
}

@ -23,16 +23,13 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
public class SLoadOperation extends AbstractOperation {
private final OptionalLong warmCost;
private final OptionalLong coldCost;
private final long warmCost;
private final long coldCost;
private final OperationResult warmSuccess;
private final OperationResult coldSuccess;
@ -40,11 +37,11 @@ public class SLoadOperation extends AbstractOperation {
public SLoadOperation(final GasCalculator gasCalculator) {
super(0x54, "SLOAD", 1, 1, 1, gasCalculator);
final long baseCost = gasCalculator.getSloadOperationGasCost();
warmCost = OptionalLong.of(baseCost + gasCalculator.getWarmStorageReadCost());
coldCost = OptionalLong.of(baseCost + gasCalculator.getColdSloadCost());
warmCost = baseCost + gasCalculator.getWarmStorageReadCost();
coldCost = baseCost + gasCalculator.getColdSloadCost();
warmSuccess = new OperationResult(warmCost, Optional.empty());
coldSuccess = new OperationResult(coldCost, Optional.empty());
warmSuccess = new OperationResult(warmCost, null);
coldSuccess = new OperationResult(coldCost, null);
}
@Override
@ -54,19 +51,18 @@ public class SLoadOperation extends AbstractOperation {
final Address address = account.getAddress();
final Bytes32 key = UInt256.fromBytes(frame.popStackItem());
final boolean slotIsWarm = frame.warmUpStorage(address, key);
final OptionalLong cost = slotIsWarm ? warmCost : coldCost;
if (frame.getRemainingGas() < cost.orElse(0L)) {
return new OperationResult(cost, Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
final long cost = slotIsWarm ? warmCost : coldCost;
if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else {
frame.pushStackItem(account.getStorageValue(UInt256.fromBytes(key)));
return slotIsWarm ? warmSuccess : coldSuccess;
}
} catch (final UnderflowException ufe) {
return new OperationResult(
warmCost, Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
return new OperationResult(warmCost, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
} catch (final OverflowException ofe) {
return new OperationResult(warmCost, Optional.of(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS));
return new OperationResult(warmCost, ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
}
}

@ -25,6 +25,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class SLtOperation extends AbstractFixedCostOperation {
static final OperationResult sltSuccess = new OperationResult(3, null);
public SLtOperation(final GasCalculator gasCalculator) {
super(0x12, "SLT", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -32,6 +34,10 @@ public class SLtOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final Bytes value0 = frame.popStackItem();
final Bytes value1 = frame.popStackItem();
@ -48,6 +54,6 @@ public class SLtOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return sltSuccess;
}
}

@ -26,6 +26,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class SModOperation extends AbstractFixedCostOperation {
private static final OperationResult smodSuccess = new OperationResult(5, null);
public SModOperation(final GasCalculator gasCalculator) {
super(0x07, "SMOD", 2, 1, 1, gasCalculator, gasCalculator.getLowTierGasCost());
}
@ -33,6 +35,10 @@ public class SModOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final Bytes value0 = frame.popStackItem();
final Bytes value1 = frame.popStackItem();
@ -63,6 +69,6 @@ public class SModOperation extends AbstractFixedCostOperation {
frame.pushStackItem(Bytes.concatenate(Bytes.wrap(padding), resultBytes));
}
return successResponse;
return smodSuccess;
}
}

@ -21,9 +21,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.units.bigints.UInt256;
public class SStoreOperation extends AbstractOperation {
@ -32,8 +29,7 @@ public class SStoreOperation extends AbstractOperation {
public static final long EIP_1706_MINIMUM = 2300L;
protected static final OperationResult ILLEGAL_STATE_CHANGE =
new OperationResult(
OptionalLong.of(0L), Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE));
new OperationResult(0L, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
private final long minimumGasRemaining;
@ -66,15 +62,11 @@ public class SStoreOperation extends AbstractOperation {
final long remainingGas = frame.getRemainingGas();
if (frame.isStatic()) {
return new OperationResult(
OptionalLong.of(remainingGas), Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE));
return new OperationResult(remainingGas, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
} else if (remainingGas < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else if (remainingGas <= minimumGasRemaining) {
return new OperationResult(
OptionalLong.of(minimumGasRemaining),
Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(minimumGasRemaining, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
// Increment the refund counter.
@ -82,6 +74,6 @@ public class SStoreOperation extends AbstractOperation {
account.setStorageValue(key, value);
frame.storageWasUpdated(key, value);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -26,6 +26,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class SarOperation extends AbstractFixedCostOperation {
static final OperationResult sarSuccess = new OperationResult(3, null);
private static final UInt256 ALL_BITS = UInt256.MAX_VALUE;
public SarOperation(final GasCalculator gasCalculator) {
@ -35,7 +37,10 @@ public class SarOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
Bytes shiftAmount = frame.popStackItem();
final Bytes value = leftPad(frame.popStackItem());
final boolean negativeNumber = value.get(0) < 0;
@ -58,6 +63,6 @@ public class SarOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
}
}
return successResponse;
return sarSuccess;
}
}

@ -24,9 +24,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import java.util.OptionalLong;
public class SelfDestructOperation extends AbstractOperation {
public SelfDestructOperation(final GasCalculator gasCalculator) {
@ -49,11 +46,9 @@ public class SelfDestructOperation extends AbstractOperation {
+ (accountIsWarm ? 0L : gasCalculator().getColdAccountAccessCost());
if (frame.isStatic()) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE));
return new OperationResult(cost, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
} else if (frame.getRemainingGas() < cost) {
return new OperationResult(
OptionalLong.of(cost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Address address = frame.getRecipientAddress();
@ -74,6 +69,6 @@ public class SelfDestructOperation extends AbstractOperation {
account.setBalance(Wei.ZERO);
frame.setState(MessageFrame.State.CODE_SUCCESS);
return new OperationResult(OptionalLong.of(cost), Optional.empty());
return new OperationResult(cost, null);
}
}

@ -25,6 +25,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class ShlOperation extends AbstractFixedCostOperation {
static final OperationResult shlSuccess = new OperationResult(3, null);
public ShlOperation(final GasCalculator gasCalculator) {
super(0x1b, "SHL", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -32,6 +34,10 @@ public class ShlOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
Bytes shiftAmount = frame.popStackItem();
if (shiftAmount.size() > 4 && (shiftAmount = shiftAmount.trimLeadingZeros()).size() > 4) {
frame.popStackItem();
@ -46,6 +52,6 @@ public class ShlOperation extends AbstractFixedCostOperation {
frame.pushStackItem(value.shiftLeft(shiftAmountInt));
}
}
return successResponse;
return shlSuccess;
}
}

@ -25,6 +25,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class ShrOperation extends AbstractFixedCostOperation {
static final OperationResult shrSuccess = new OperationResult(3, null);
public ShrOperation(final GasCalculator gasCalculator) {
super(0x1c, "SHR", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -32,6 +34,10 @@ public class ShrOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
Bytes shiftAmount = frame.popStackItem();
if (shiftAmount.size() > 4 && (shiftAmount = shiftAmount.trimLeadingZeros()).size() > 4) {
frame.popStackItem();
@ -46,6 +52,6 @@ public class ShrOperation extends AbstractFixedCostOperation {
frame.pushStackItem(value.shiftRight(shiftAmountInt));
}
}
return successResponse;
return shrSuccess;
}
}

@ -23,6 +23,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class SignExtendOperation extends AbstractFixedCostOperation {
private static final OperationResult signExtendSuccess = new OperationResult(5, null);
public SignExtendOperation(final GasCalculator gasCalculator) {
super(0x0B, "SIGNEXTEND", 2, 1, 1, gasCalculator, gasCalculator.getLowTierGasCost());
}
@ -30,32 +32,28 @@ public class SignExtendOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
// Stack items are reversed for the SIGNEXTEND operation.
final UInt256 result = signExtend(value1, value0);
frame.pushStackItem(result);
return successResponse;
}
private static UInt256 signExtend(final UInt256 v1, final UInt256 v2) {
final MutableBytes32 result = MutableBytes32.create();
// Any value >= 31 imply an index <= 0, so no work to do (note that 0 itself is a valid index,
// but copying the 0th byte to itself is only so useful).
if (!v2.fitsInt() || v2.intValue() >= 31) {
v1.copyTo(result);
return UInt256.fromBytes(result);
if (!value0.fitsInt() || value0.intValue() >= 31) {
frame.pushStackItem(value1);
} else {
// This is safe, since other < 31.
final int byteIndex = 32 - 1 - value0.getInt(32 - 4);
final byte toSet = value1.get(byteIndex) < 0 ? (byte) 0xFF : 0x00;
result.mutableSlice(0, byteIndex).fill(toSet);
value1.slice(byteIndex).copyTo(result, byteIndex);
frame.pushStackItem(UInt256.fromBytes(result));
}
// This is safe, since other < 31.
final int byteIndex = 32 - 1 - v2.getInt(32 - 4);
final byte toSet = v1.get(byteIndex) < 0 ? (byte) 0xFF : 0x00;
result.mutableSlice(0, byteIndex).fill(toSet);
v1.slice(byteIndex).copyTo(result, byteIndex);
return UInt256.fromBytes(result);
return signExtendSuccess;
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.bytes.Bytes;
public class StopOperation extends AbstractFixedCostOperation {
static final OperationResult stopSuccess = new OperationResult(0, null);
public StopOperation(final GasCalculator gasCalculator) {
super(0x00, "STOP", 0, 0, 1, gasCalculator, gasCalculator.getZeroTierGasCost());
}
@ -29,8 +31,12 @@ public class StopOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
frame.setState(MessageFrame.State.CODE_SUCCESS);
frame.setOutputData(Bytes.EMPTY);
return successResponse;
return stopSuccess;
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class SubOperation extends AbstractFixedCostOperation {
static final OperationResult subSuccess = new OperationResult(3, null);
public SubOperation(final GasCalculator gasCalculator) {
super(0x03, "SUB", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,6 +31,10 @@ public class SubOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
@ -36,6 +42,6 @@ public class SubOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return subSuccess;
}
}

@ -19,19 +19,19 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.tuweni.bytes.Bytes;
public class SwapOperation extends AbstractFixedCostOperation {
public static final int SWAP_BASE = 0x8F;
static final OperationResult swapSuccess = new OperationResult(3, null);
private final int index;
protected final Operation.OperationResult underflowResponse;
public SwapOperation(final int index, final GasCalculator gasCalculator) {
super(
0x90 + index - 1,
SWAP_BASE + index,
"SWAP" + index,
index + 1,
index + 1,
@ -40,22 +40,20 @@ public class SwapOperation extends AbstractFixedCostOperation {
gasCalculator.getVeryLowTierGasCost());
this.index = index;
this.underflowResponse =
new Operation.OperationResult(
OptionalLong.of(gasCost), Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS));
new Operation.OperationResult(gasCost, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
}
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
// getStackItem doesn't under/overflow. Check explicitly.
if (frame.stackSize() < getStackItemsConsumed()) {
return underflowResponse;
}
return staticOperation(frame, index);
}
public static OperationResult staticOperation(final MessageFrame frame, final int index) {
final Bytes tmp = frame.getStackItem(0);
frame.setStackItem(0, frame.getStackItem(index));
frame.setStackItem(index, tmp);
return successResponse;
return swapSuccess;
}
}

@ -22,6 +22,8 @@ import org.apache.tuweni.units.bigints.UInt256;
public class XorOperation extends AbstractFixedCostOperation {
static final OperationResult xorSuccess = new OperationResult(3, null);
public XorOperation(final GasCalculator gasCalculator) {
super(0x18, "XOR", 2, 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost());
}
@ -29,6 +31,10 @@ public class XorOperation extends AbstractFixedCostOperation {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
return staticOperation(frame);
}
public static OperationResult staticOperation(final MessageFrame frame) {
final UInt256 value0 = UInt256.fromBytes(frame.popStackItem());
final UInt256 value1 = UInt256.fromBytes(frame.popStackItem());
@ -36,6 +42,6 @@ public class XorOperation extends AbstractFixedCostOperation {
frame.pushStackItem(result);
return successResponse;
return xorSuccess;
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.evm.tracing;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.operation.SStoreOperation;
public class EstimateGasOperationTracer implements OperationTracer {
@ -24,18 +25,13 @@ public class EstimateGasOperationTracer implements OperationTracer {
private long sStoreStipendNeeded = 0L;
@Override
public void traceExecution(
final MessageFrame frame, final OperationTracer.ExecuteOperation executeOperation) {
try {
executeOperation.execute();
} finally {
if (frame.getCurrentOperation() instanceof SStoreOperation && sStoreStipendNeeded == 0L) {
sStoreStipendNeeded =
((SStoreOperation) frame.getCurrentOperation()).getMinimumGasRemaining();
}
if (maxDepth < frame.getMessageStackDepth()) {
maxDepth = frame.getMessageStackDepth();
}
public void tracePostExecution(final MessageFrame frame, final OperationResult operationResult) {
if (frame.getCurrentOperation() instanceof SStoreOperation && sStoreStipendNeeded == 0L) {
sStoreStipendNeeded =
((SStoreOperation) frame.getCurrentOperation()).getMinimumGasRemaining();
}
if (maxDepth < frame.getMessageStackDepth()) {
maxDepth = frame.getMessageStackDepth();
}
}

@ -24,9 +24,12 @@ import org.apache.tuweni.bytes.Bytes;
public interface OperationTracer {
OperationTracer NO_TRACING = ((frame, executeOperation) -> executeOperation.execute());
OperationTracer NO_TRACING = new OperationTracer() {};
void traceExecution(MessageFrame frame, ExecuteOperation executeOperation);
default void tracePreExecution(final MessageFrame frame) {}
default void tracePostExecution(
final MessageFrame frame, final OperationResult operationResult) {}
default void tracePrecompileCall(
final MessageFrame frame, final long gasRequirement, final Bytes output) {}

@ -55,32 +55,33 @@ public class StandardJsonTracer implements OperationTracer {
final Joiner commaJoiner = Joiner.on(',');
private List<String> preExecuteStack;
@Override
public void tracePreExecution(final MessageFrame messageFrame) {
preExecuteStack = new ArrayList<>(messageFrame.stackSize());
for (int i = messageFrame.stackSize() - 1; i >= 0; i--) {
preExecuteStack.add("\"" + shortBytes(messageFrame.getStackItem(i)) + "\"");
}
}
@Override
public void traceExecution(
final MessageFrame messageFrame, final ExecuteOperation executeOperation) {
public void tracePostExecution(
final MessageFrame messageFrame, final Operation.OperationResult executeResult) {
final Operation currentOp = messageFrame.getCurrentOperation();
final int pc = messageFrame.getPC();
final int opcode = currentOp.getOpcode();
final String remainingGas = shortNumber(messageFrame.getRemainingGas());
final List<String> stack = new ArrayList<>(messageFrame.stackSize());
for (int i = messageFrame.stackSize() - 1; i >= 0; i--) {
stack.add("\"" + shortBytes(messageFrame.getStackItem(i)) + "\"");
}
final Bytes returnData = messageFrame.getReturnData();
final int depth = messageFrame.getMessageStackDepth() + 1;
final Operation.OperationResult executeResult = executeOperation.execute();
final StringBuilder sb = new StringBuilder(1024);
sb.append("{");
sb.append("\"pc\":").append(pc).append(",");
sb.append("\"op\":").append(opcode).append(",");
sb.append("\"gas\":\"").append(remainingGas).append("\",");
sb.append("\"gasCost\":\"")
.append(
executeResult.getGasCost().isPresent()
? shortNumber(executeResult.getGasCost().getAsLong())
: "")
.append(executeResult.getGasCost() != 0 ? shortNumber(executeResult.getGasCost()) : "")
.append("\",");
if (showMemory) {
final Bytes memory = messageFrame.readMemory(0, messageFrame.memoryWordSize() * 32L);
@ -90,7 +91,7 @@ public class StandardJsonTracer implements OperationTracer {
sb.append("\"memory\":\"0x\",");
sb.append("\"memSize\":").append(messageFrame.memoryByteSize()).append(",");
}
sb.append("\"stack\":[").append(commaJoiner.join(stack)).append("],");
sb.append("\"stack\":[").append(commaJoiner.join(preExecuteStack)).append("],");
sb.append("\"returnData\":")
.append(returnData.size() > 0 ? '"' + returnData.toHexString() + '"' : "null")
.append(",");
@ -99,11 +100,9 @@ public class StandardJsonTracer implements OperationTracer {
sb.append("\"opName\":\"").append(currentOp.getName()).append("\",");
sb.append("\"error\":\"")
.append(
executeResult
.getHaltReason()
.map(ExceptionalHaltReason::getDescription)
.orElse(
messageFrame.getRevertReason().map(StandardJsonTracer::quoteEscape).orElse("")))
executeResult.getHaltReason() == null
? (quoteEscape(messageFrame.getRevertReason().orElse(Bytes.EMPTY)))
: executeResult.getHaltReason().getDescription())
.append("\"}");
out.println(sb);
}
@ -141,9 +140,13 @@ public class StandardJsonTracer implements OperationTracer {
@Override
public void tracePrecompileCall(
final MessageFrame frame, final long gasRequirement, final Bytes output) {}
final MessageFrame frame, final long gasRequirement, final Bytes output) {
// precompile calls are not part of the standard trace
}
@Override
public void traceAccountCreationResult(
final MessageFrame frame, final Optional<ExceptionalHaltReason> haltReason) {}
final MessageFrame frame, final Optional<ExceptionalHaltReason> haltReason) {
// precompile calls are not part of the standard trace
}
}

@ -12,14 +12,13 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.vm;
package org.hyperledger.besu.evm.internal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.hyperledger.besu.evm.internal.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;
import org.hyperledger.besu.evm.internal.OperandStack;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
@ -30,7 +29,7 @@ public class OperandStackTest {
@Test
public void construction() {
final OperandStack stack = new OperandStack(1);
assertThat(stack.size()).isEqualTo(0);
assertThat(stack.size()).isZero();
}
@Test
@ -89,7 +88,7 @@ public class OperandStackTest {
public void set_NegativeOffset() {
final OperandStack stack = new OperandStack(1);
final Bytes32 operand = Bytes32.fromHexString("0x01");
assertThatThrownBy(() -> stack.set(-1, operand)).isInstanceOf(IndexOutOfBoundsException.class);
assertThatThrownBy(() -> stack.set(-1, operand)).isInstanceOf(UnderflowException.class);
}
@Test
@ -97,7 +96,7 @@ public class OperandStackTest {
final OperandStack stack = new OperandStack(1);
stack.push(UInt256.fromHexString("0x01"));
final Bytes32 operand = Bytes32.fromHexString("0x01");
assertThatThrownBy(() -> stack.set(2, operand)).isInstanceOf(IndexOutOfBoundsException.class);
assertThatThrownBy(() -> stack.set(2, operand)).isInstanceOf(OverflowException.class);
}
@Test
Loading…
Cancel
Save