EVM: Move from an EnumSet for halt reason to a simple Optional. (#1089)

Internally we used to use an enum set to track halt reasons and we would
track multiple halt reasons.  However, what the halt reason is does not
matter to reference tests and tracing, only that a halt occurred.
Repalace the EnumSet with an Optional and trace only one revert reason.
This saves us time in enumset management and also allows us to return
quicker once any halt is detected.

Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
pull/1105/head
Danno Ferrin 4 years ago committed by GitHub
parent 256e5c914d
commit 11a3329ef7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/DebugTraceTransactionResult.java
  2. 9
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/StructLogWithError.java
  3. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java
  4. 13
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java
  5. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java
  6. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java
  7. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java
  8. 6
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java
  9. 17
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/debug/TraceFrame.java
  10. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java
  11. 37
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/EVM.java
  12. 17
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java
  13. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/Operation.java
  14. 14
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/ehalt/ExceptionalHaltException.java
  15. 54
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/ehalt/ExceptionalHaltManager.java
  16. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/AbstractCreateOperation.java
  17. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/BeginSubOperation.java
  18. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/CallOperation.java
  19. 13
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/InvalidOperation.java
  20. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperation.java
  21. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/JumpSubOperation.java
  22. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/JumpiOperation.java
  23. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/LogOperation.java
  24. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/ReturnDataCopyOperation.java
  25. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/ReturnSubOperation.java
  26. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java
  27. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SelfDestructOperation.java
  28. 5
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java
  29. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/BeginSubOperationTest.java
  30. 8
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperationTest.java
  31. 14
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpSubOperationTest.java
  32. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/ReturnSubOperationTest.java
  33. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperationTest.java
  34. 8
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java

@ -48,9 +48,10 @@ public class DebugTraceTransactionResult {
}
private static StructLog createStructLog(final TraceFrame frame) {
return frame.getExceptionalHaltReasons().isEmpty()
? new StructLog(frame)
: new StructLogWithError(frame);
return frame
.getExceptionalHaltReason()
.map(__ -> (StructLog) new StructLogWithError(frame))
.orElse(new StructLog(frame));
}
@JsonGetter(value = "structLogs")

@ -15,7 +15,6 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import com.fasterxml.jackson.annotation.JsonGetter;
@ -23,14 +22,10 @@ public class StructLogWithError extends StructLog {
private final String[] error;
public StructLogWithError(final TraceFrame traceFrame) {
StructLogWithError(final TraceFrame traceFrame) {
super(traceFrame);
error =
traceFrame.getExceptionalHaltReasons().isEmpty()
? null
: traceFrame.getExceptionalHaltReasons().stream()
.map(ExceptionalHaltReason::name)
.toArray(String[]::new);
traceFrame.getExceptionalHaltReason().map(ehr -> new String[] {ehr.name()}).orElse(null);
}
@JsonGetter("error")

@ -167,7 +167,7 @@ public class FlatTraceGenerator {
if (currentContext != null) currentContext = handleRevert(tracesContexts, currentContext);
}
if (currentContext != null && !traceFrame.getExceptionalHaltReasons().isEmpty()) {
if (currentContext != null && traceFrame.getExceptionalHaltReason().isPresent()) {
currentContext = handleHalt(tracesContexts, currentContext, traceFrame);
}
}
@ -435,9 +435,7 @@ public class FlatTraceGenerator {
final TraceFrame traceFrame) {
final FlatTrace.Builder traceFrameBuilder = currentContext.getBuilder();
traceFrameBuilder.error(
traceFrame.getExceptionalHaltReasons().stream()
.map(ExceptionalHaltReason::getDescription)
.reduce((a, b) -> a + ", " + b));
traceFrame.getExceptionalHaltReason().map(ExceptionalHaltReason::getDescription));
final Action.Builder actionBuilder = traceFrameBuilder.getActionBuilder();
actionBuilder.value(Quantity.create(traceFrame.getValue()));
tracesContexts.removeLast();

@ -24,7 +24,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Optional;
import java.util.stream.IntStream;
@ -105,10 +104,10 @@ public class VmTraceGenerator {
private boolean mustIgnore(final TraceFrame frame) {
if ("STOP".equals(frame.getOpcode()) && transactionTrace.getTraceFrames().size() == 1) {
return true;
} else if (!frame.getExceptionalHaltReasons().isEmpty()) {
final EnumSet<ExceptionalHaltReason> haltReasons = frame.getExceptionalHaltReasons();
return !haltReasons.contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION)
&& !haltReasons.contains(ExceptionalHaltReason.INSUFFICIENT_GAS);
} else if (frame.getExceptionalHaltReason().isPresent()) {
final Optional<ExceptionalHaltReason> haltReason = frame.getExceptionalHaltReason();
return haltReason.get() != ExceptionalHaltReason.INVALID_JUMP_DESTINATION
&& haltReason.get() != ExceptionalHaltReason.INSUFFICIENT_GAS;
} else {
return frame.isVirtualOperation();
}
@ -117,7 +116,9 @@ public class VmTraceGenerator {
private void completeStep(
final TraceFrame frame, final VmOperation op, final VmOperationExecutionReport report) {
// add the operation representation to the list of traces
if (frame.getExceptionalHaltReasons().contains(ExceptionalHaltReason.INSUFFICIENT_GAS)) {
final Optional<ExceptionalHaltReason> exceptionalHaltReason = frame.getExceptionalHaltReason();
if (exceptionalHaltReason.isPresent()
&& exceptionalHaltReason.get() == ExceptionalHaltReason.INSUFFICIENT_GAS) {
op.setVmOperationExecutionReport(null);
} else {
op.setVmOperationExecutionReport(report);

@ -31,11 +31,9 @@ import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
@ -69,7 +67,7 @@ public class DebugTraceBlockByHashTest {
Optional.of(Gas.of(56)),
Gas.ZERO,
2,
EnumSet.noneOf(ExceptionalHaltReason.class),
Optional.empty(),
null,
Wei.ZERO,
Bytes.EMPTY,

@ -35,10 +35,8 @@ import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
@ -74,7 +72,7 @@ public class DebugTraceBlockByNumberTest {
Optional.of(Gas.of(56)),
Gas.ZERO,
2,
EnumSet.noneOf(ExceptionalHaltReason.class),
Optional.empty(),
null,
Wei.ZERO,
Bytes.EMPTY,

@ -38,11 +38,9 @@ import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
@ -87,7 +85,7 @@ public class DebugTraceBlockTest {
Optional.of(Gas.of(56)),
Gas.ZERO,
2,
EnumSet.noneOf(ExceptionalHaltReason.class),
Optional.empty(),
null,
Wei.ZERO,
Bytes.EMPTY,

@ -36,10 +36,8 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor.Result;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -86,7 +84,7 @@ public class DebugTraceTransactionTest {
Optional.of(Gas.of(56)),
Gas.ZERO,
2,
EnumSet.noneOf(ExceptionalHaltReason.class),
Optional.empty(),
null,
Wei.ZERO,
Bytes.EMPTY,
@ -143,7 +141,7 @@ public class DebugTraceTransactionTest {
Optional.of(Gas.of(56)),
Gas.ZERO,
2,
EnumSet.noneOf(ExceptionalHaltReason.class),
Optional.empty(),
null,
Wei.ZERO,
Bytes.EMPTY,

@ -22,7 +22,6 @@ import org.hyperledger.besu.ethereum.vm.Code;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.internal.MemoryEntry;
import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
@ -39,7 +38,7 @@ public class TraceFrame {
private final Optional<Gas> gasCost;
private final Gas gasRefund;
private final int depth;
private final EnumSet<ExceptionalHaltReason> exceptionalHaltReasons;
private Optional<ExceptionalHaltReason> exceptionalHaltReason;
private final Address recipient;
private final Wei value;
private final Bytes inputData;
@ -67,7 +66,7 @@ public class TraceFrame {
final Optional<Gas> gasCost,
final Gas gasRefund,
final int depth,
final EnumSet<ExceptionalHaltReason> exceptionalHaltReasons,
final Optional<ExceptionalHaltReason> exceptionalHaltReason,
final Address recipient,
final Wei value,
final Bytes inputData,
@ -90,7 +89,7 @@ public class TraceFrame {
this.gasCost = gasCost;
this.gasRefund = gasRefund;
this.depth = depth;
this.exceptionalHaltReasons = exceptionalHaltReasons;
this.exceptionalHaltReason = exceptionalHaltReason;
this.recipient = recipient;
this.value = value;
this.inputData = inputData;
@ -134,12 +133,12 @@ public class TraceFrame {
return depth;
}
public EnumSet<ExceptionalHaltReason> getExceptionalHaltReasons() {
return exceptionalHaltReasons;
public Optional<ExceptionalHaltReason> getExceptionalHaltReason() {
return exceptionalHaltReason;
}
public boolean addExceptionalHaltReason(final ExceptionalHaltReason exceptionalHaltReason) {
return exceptionalHaltReasons.add(exceptionalHaltReason);
public void setExceptionalHaltReason(final ExceptionalHaltReason exceptionalHaltReason) {
this.exceptionalHaltReason = Optional.ofNullable(exceptionalHaltReason);
}
public Address getRecipient() {
@ -190,7 +189,7 @@ public class TraceFrame {
.add("getGasRemaining", gasRemaining)
.add("gasCost", gasCost)
.add("depth", depth)
.add("exceptionalHaltReasons", exceptionalHaltReasons)
.add("exceptionalHaltReason", exceptionalHaltReason)
.add("stack", stack)
.add("memory", memory)
.add("storage", storage)

@ -24,7 +24,6 @@ import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.vm.ehalt.ExceptionalHaltException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -57,8 +56,7 @@ public class DebugOperationTracer implements OperationTracer {
final String opcode = currentOperation.getName();
final int pc = frame.getPC();
final Gas gasRemaining = frame.getRemainingGas();
final EnumSet<ExceptionalHaltReason> exceptionalHaltReasons =
EnumSet.copyOf(frame.getExceptionalHaltReasons());
final Optional<ExceptionalHaltReason> exceptionalHaltReason = frame.getExceptionalHaltReason();
final Bytes inputData = frame.getInputData();
final Optional<Bytes32[]> stack = captureStack(frame);
final WorldUpdater worldUpdater = frame.getWorldState();
@ -83,7 +81,7 @@ public class DebugOperationTracer implements OperationTracer {
currentGasCost,
frame.getGasRefund(),
depth,
exceptionalHaltReasons,
exceptionalHaltReason,
frame.getRecipientAddress(),
frame.getApparentValue(),
inputData,
@ -127,7 +125,7 @@ public class DebugOperationTracer implements OperationTracer {
}
frameIndex--;
} while (foundTraceFrame == null);
foundTraceFrame.addExceptionalHaltReason(exceptionalHaltReason);
foundTraceFrame.setExceptionalHaltReason(exceptionalHaltReason);
}
});
}

@ -19,12 +19,10 @@ import static org.apache.logging.log4j.LogManager.getLogger;
import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.ethereum.vm.MessageFrame.State;
import org.hyperledger.besu.ethereum.vm.ehalt.ExceptionalHaltException;
import org.hyperledger.besu.ethereum.vm.ehalt.ExceptionalHaltManager;
import org.hyperledger.besu.ethereum.vm.operations.InvalidOperation;
import org.hyperledger.besu.ethereum.vm.operations.StopOperation;
import org.hyperledger.besu.ethereum.vm.operations.VirtualOperation;
import java.util.EnumSet;
import java.util.Optional;
import java.util.function.BiConsumer;
@ -70,7 +68,7 @@ public class EVM {
throws ExceptionalHaltException {
frame.setCurrentOperation(
operationAtOffset(frame.getCode(), frame.getContractAccountVersion(), frame.getPC()));
evaluateExceptionalHaltReasons(frame);
frame.setExceptionalHaltReason(checkExceptionalHalt(frame, this));
final Optional<Gas> currentGasCost = calculateGasCost(frame);
operationTracer.traceExecution(
frame,
@ -84,19 +82,30 @@ public class EVM {
});
}
private void evaluateExceptionalHaltReasons(final MessageFrame frame) {
final EnumSet<ExceptionalHaltReason> haltReasons =
ExceptionalHaltManager.evaluateAll(frame, this);
frame.getExceptionalHaltReasons().addAll(haltReasons);
public static Optional<ExceptionalHaltReason> checkExceptionalHalt(
final MessageFrame frame, final EVM evm) {
final Operation op = frame.getCurrentOperation();
if (frame.stackSize() + op.getStackSizeChange() > frame.getMaxStackSize()) {
return Optional.of(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
if (frame.stackSize() < op.getStackItemsConsumed()) {
return Optional.of(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
}
if (frame.getRemainingGas().compareTo(op.cost(frame)) < 0) {
return Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS);
}
return op.exceptionalHaltCondition(frame, evm);
}
private Optional<Gas> calculateGasCost(final MessageFrame frame) {
// Calculate the cost if, and only if, we are not halting as a result of a stack underflow, as
// the operation may need all its stack items to calculate gas.
// This is how existing EVM implementations behave.
if (!frame
.getExceptionalHaltReasons()
.contains(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS)) {
final Optional<ExceptionalHaltReason> exceptionalHaltReason = frame.getExceptionalHaltReason();
if (exceptionalHaltReason.isEmpty()
|| exceptionalHaltReason.get() != ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS) {
try {
return Optional.ofNullable(frame.getCurrentOperation().cost(frame));
} catch (final IllegalArgumentException e) {
@ -112,12 +121,12 @@ public class EVM {
}
private void checkForExceptionalHalt(final MessageFrame frame) throws ExceptionalHaltException {
final EnumSet<ExceptionalHaltReason> exceptionalHaltReasons = frame.getExceptionalHaltReasons();
if (!exceptionalHaltReasons.isEmpty()) {
LOG.trace("MessageFrame evaluation halted because of {}", exceptionalHaltReasons);
final Optional<ExceptionalHaltReason> exceptionalHaltReason = frame.getExceptionalHaltReason();
if (exceptionalHaltReason.isPresent()) {
LOG.trace("MessageFrame evaluation halted because of {}", exceptionalHaltReason);
frame.setState(State.EXCEPTIONAL_HALT);
frame.setOutputData(Bytes.EMPTY);
throw new ExceptionalHaltException(exceptionalHaltReasons);
throw new ExceptionalHaltException(exceptionalHaltReason.get());
}
}

@ -32,7 +32,6 @@ import org.hyperledger.besu.ethereum.vm.operations.ReturnStack;
import java.util.ArrayList;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -238,8 +237,7 @@ public class MessageFrame {
private final Hash transactionHash;
// Miscellaneous fields.
private final EnumSet<ExceptionalHaltReason> exceptionalHaltReasons =
EnumSet.noneOf(ExceptionalHaltReason.class);
private Optional<ExceptionalHaltReason> exceptionalHaltReason = Optional.empty();
private Operation currentOperation;
private final Consumer<MessageFrame> completer;
private Optional<MemoryEntry> maybeUpdatedMemory = Optional.empty();
@ -985,8 +983,17 @@ public class MessageFrame {
return messageFrameStack;
}
public EnumSet<ExceptionalHaltReason> getExceptionalHaltReasons() {
return exceptionalHaltReasons;
public void setExceptionalHaltReason(final ExceptionalHaltReason exceptionalHaltReason) {
this.exceptionalHaltReason = Optional.of(exceptionalHaltReason);
}
public void setExceptionalHaltReason(
final Optional<ExceptionalHaltReason> exceptionalHaltReason) {
this.exceptionalHaltReason = exceptionalHaltReason;
}
public Optional<ExceptionalHaltReason> getExceptionalHaltReason() {
return exceptionalHaltReason;
}
/**

@ -15,12 +15,10 @@
package org.hyperledger.besu.ethereum.vm;
import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.ethereum.vm.ehalt.ExceptionalHaltPredicate;
import java.util.EnumSet;
import java.util.Optional;
public interface Operation extends ExceptionalHaltPredicate {
public interface Operation {
/**
* Gas cost of this operation, in context of the provided frame.
@ -42,16 +40,12 @@ public interface Operation extends ExceptionalHaltPredicate {
* Check if an exceptional halt condition should apply
*
* @param frame the current frame
* @param previousReasons any existing exceptional halt conditions
* @param evm the currently executing EVM
* @return an {@link Optional} containing the {@link ExceptionalHaltReason} that applies or empty
* if no exceptional halt condition applies.
*/
@Override
default Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
return Optional.empty();
}

@ -16,22 +16,20 @@ package org.hyperledger.besu.ethereum.vm.ehalt;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import java.util.EnumSet;
/** An exception to signal that an exceptional halt has occurred. */
public class ExceptionalHaltException extends Exception {
private final EnumSet<ExceptionalHaltReason> reasons;
private final ExceptionalHaltReason reason;
public ExceptionalHaltException(final EnumSet<ExceptionalHaltReason> reasons) {
this.reasons = reasons;
public ExceptionalHaltException(final ExceptionalHaltReason reason) {
this.reason = reason;
}
@Override
public String getMessage() {
return "Exceptional halt condition(s) triggered: " + this.reasons;
return "Exceptional halt condition(s) triggered: " + this.reason;
}
public EnumSet<ExceptionalHaltReason> getReasons() {
return reasons;
public ExceptionalHaltReason getReasons() {
return reason;
}
}

@ -1,54 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.vm.ehalt;
import org.hyperledger.besu.ethereum.vm.EVM;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
public class ExceptionalHaltManager {
private static final List<ExceptionalHaltPredicate> GLOBAL =
Arrays.asList(
new InvalidOperationExceptionalHaltPredicate(),
new StackOverflowExceptionalHaltPredicate(),
new StackUnderflowExceptionalHaltPredicate(),
new InsufficientGasExceptionalHaltPredicate());
public static EnumSet<ExceptionalHaltReason> evaluateAll(
final MessageFrame frame, final EVM evm) {
final EnumSet<ExceptionalHaltReason> answer = EnumSet.noneOf(ExceptionalHaltReason.class);
for (final ExceptionalHaltPredicate predicate : GLOBAL) {
predicate.exceptionalHaltCondition(frame, answer, evm).ifPresent(answer::add);
}
// TODO: Determine whether or not to short-circuit here.
// Several operations (e.g. JUMP and JUMPI) have stack dependent checks.
if (!answer.contains(ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS)) {
// Evaluate any operation specific halt conditions too.
frame
.getCurrentOperation()
.exceptionalHaltCondition(frame, answer, evm)
.ifPresent(answer::add);
}
return answer;
}
}

@ -26,7 +26,6 @@ import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.Words;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
@ -73,9 +72,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
return frame.isStatic()
? Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE)
: Optional.empty();

@ -21,7 +21,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.EnumSet;
import java.util.Optional;
public class BeginSubOperation extends AbstractOperation {
@ -42,9 +41,7 @@ public class BeginSubOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
return Optional.of(ExceptionalHaltReason.INVALID_SUB_ROUTINE_ENTRY);
}
}

@ -25,7 +25,6 @@ import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.Words;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
@ -119,9 +118,7 @@ public class CallOperation extends AbstractCallOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
return frame.isStatic() && !value(frame).isZero()
? Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE)
: Optional.empty();

@ -16,10 +16,13 @@ package org.hyperledger.besu.ethereum.vm.operations;
import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.ethereum.vm.AbstractOperation;
import org.hyperledger.besu.ethereum.vm.EVM;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.Optional;
public class InvalidOperation extends AbstractOperation {
public InvalidOperation(final GasCalculator gasCalculator) {
@ -28,12 +31,18 @@ public class InvalidOperation extends AbstractOperation {
@Override
public Gas cost(final MessageFrame frame) {
return null;
return Gas.ZERO;
}
@Override
public void execute(final MessageFrame frame) {
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
frame.getExceptionalHaltReasons().add(ExceptionalHaltReason.INVALID_OPERATION);
frame.setExceptionalHaltReason(ExceptionalHaltReason.INVALID_OPERATION);
}
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame, final EVM evm) {
return Optional.of(ExceptionalHaltReason.INVALID_OPERATION);
}
}

@ -22,7 +22,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
@ -46,9 +45,7 @@ public class JumpOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
final Code code = frame.getCode();
final UInt256 potentialJumpDestination = UInt256.fromBytes(frame.getStackItem(0));

@ -22,7 +22,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
@ -47,9 +46,7 @@ public class JumpSubOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
final Code code = frame.getCode();
if (frame.isReturnStackFull()) {

@ -22,7 +22,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
@ -53,9 +52,7 @@ public class JumpiOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
// If condition is zero (false), no jump is will be performed. Therefore skip the test.
if (frame.getStackItem(1).isZero()) {
return Optional.empty();

@ -24,7 +24,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.EnumSet;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
@ -67,9 +66,7 @@ public class LogOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
return frame.isStatic()
? Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE)
: Optional.empty();

@ -21,7 +21,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
@ -54,9 +53,7 @@ public class ReturnDataCopyOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
final Bytes returnData = frame.getReturnData();
final UInt256 start = UInt256.fromBytes(frame.getStackItem(1));

@ -21,7 +21,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.EnumSet;
import java.util.Optional;
public class ReturnSubOperation extends AbstractOperation {
@ -42,9 +41,7 @@ public class ReturnSubOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
if (frame.isReturnStackEmpty()) {
return Optional.of(ExceptionalHaltReason.INVALID_RETSUB);
}

@ -23,7 +23,6 @@ import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.EnumSet;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
@ -71,9 +70,7 @@ public class SStoreOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
if (frame.isStatic()) {
return Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
} else if (frame.getRemainingGas().compareTo(minumumGasRemaining) <= 0) {

@ -26,7 +26,6 @@ import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.Words;
import java.util.EnumSet;
import java.util.Optional;
public class SelfDestructOperation extends AbstractOperation {
@ -69,9 +68,7 @@ public class SelfDestructOperation extends AbstractOperation {
@Override
public Optional<ExceptionalHaltReason> exceptionalHaltCondition(
final MessageFrame frame,
final EnumSet<ExceptionalHaltReason> previousReasons,
final EVM evm) {
final MessageFrame frame, final EVM evm) {
return frame.isStatic()
? Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE)
: Optional.empty();

@ -32,7 +32,6 @@ import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.vm.ehalt.ExceptionalHaltException;
import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
@ -157,10 +156,8 @@ public class DebugOperationTracerTest {
@Test
public void shouldCaptureFrameWhenExceptionalHaltOccurs() throws Exception {
final EnumSet<ExceptionalHaltReason> expectedHaltReasons =
EnumSet.of(ExceptionalHaltReason.INSUFFICIENT_GAS);
final ExceptionalHaltException expectedException =
new ExceptionalHaltException(expectedHaltReasons);
new ExceptionalHaltException(ExceptionalHaltReason.INSUFFICIENT_GAS);
doThrow(expectedException).when(executeOperationAction).execute();
final MessageFrame frame = validMessageFrame();
final Map<UInt256, UInt256> updatedStorage = setupStorageForCapture(frame);

@ -92,7 +92,7 @@ public class BeginSubOperationTest {
.returnStack(new ReturnStack())
.build();
frame.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frame, null, null))
assertThat(operation.exceptionalHaltCondition(frame, null))
.contains(ExceptionalHaltReason.INVALID_SUB_ROUTINE_ENTRY);
}
}

@ -88,7 +88,7 @@ public class JumpOperationTest {
.build();
frame.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frame, null, evm)).isNotPresent();
assertThat(operation.exceptionalHaltCondition(frame, evm)).isNotPresent();
operation.execute(frame);
}
@ -102,7 +102,7 @@ public class JumpOperationTest {
.build();
frame.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frame, null, evm)).isNotPresent();
assertThat(operation.exceptionalHaltCondition(frame, evm)).isNotPresent();
operation.execute(frame);
}
@ -116,7 +116,7 @@ public class JumpOperationTest {
.build();
frameDestinationGreaterThanCodeSize.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frameDestinationGreaterThanCodeSize, null, null))
assertThat(operation.exceptionalHaltCondition(frameDestinationGreaterThanCodeSize, null))
.contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
final MessageFrame frameDestinationEqualsToCodeSize =
@ -127,7 +127,7 @@ public class JumpOperationTest {
.build();
frameDestinationEqualsToCodeSize.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frameDestinationEqualsToCodeSize, null, null))
assertThat(operation.exceptionalHaltCondition(frameDestinationEqualsToCodeSize, null))
.contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
}
}

@ -103,7 +103,7 @@ public class JumpSubOperationTest {
.returnStack(new ReturnStack())
.build();
frame.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frame, null, evm))
assertThat(operation.exceptionalHaltCondition(frame, evm))
.contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
}
@ -118,7 +118,7 @@ public class JumpSubOperationTest {
.build();
frame.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frame, null, evm)).isNotPresent();
assertThat(operation.exceptionalHaltCondition(frame, evm)).isNotPresent();
operation.execute(frame);
assertThat(frame.popReturnStackItem()).isEqualTo(CURRENT_PC + 1);
}
@ -134,7 +134,7 @@ public class JumpSubOperationTest {
.build();
frame.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frame, null, evm)).isNotPresent();
assertThat(operation.exceptionalHaltCondition(frame, evm)).isNotPresent();
operation.execute(frame);
assertThat(frame.popReturnStackItem()).isEqualTo(CURRENT_PC + 1);
}
@ -150,7 +150,7 @@ public class JumpSubOperationTest {
.build();
frameDestinationGreaterThanCodeSize.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frameDestinationGreaterThanCodeSize, null, null))
assertThat(operation.exceptionalHaltCondition(frameDestinationGreaterThanCodeSize, null))
.contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
final MessageFrame frameDestinationEqualsToCodeSize =
@ -161,7 +161,7 @@ public class JumpSubOperationTest {
.build();
frameDestinationEqualsToCodeSize.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frameDestinationEqualsToCodeSize, null, null))
assertThat(operation.exceptionalHaltCondition(frameDestinationEqualsToCodeSize, null))
.contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
}
@ -172,7 +172,7 @@ public class JumpSubOperationTest {
createMessageFrameBuilder(Gas.of(1)).returnStack(new ReturnStack()).build();
frame.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frame, null, null))
assertThat(operation.exceptionalHaltCondition(frame, null))
.contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION);
}
@ -189,7 +189,7 @@ public class JumpSubOperationTest {
IntStream.range(0, 1023).forEach(frame::pushReturnStackItem);
assertThat(frame.isReturnStackFull()).isTrue();
assertThat(returnStack.size()).isEqualTo(1023);
assertThat(operation.exceptionalHaltCondition(frame, null, null))
assertThat(operation.exceptionalHaltCondition(frame, null))
.contains(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
}

@ -85,7 +85,7 @@ public class ReturnSubOperationTest {
final MessageFrame frame =
createMessageFrameBuilder(Gas.of(1)).returnStack(new ReturnStack()).build();
frame.setPC(CURRENT_PC);
assertThat(operation.exceptionalHaltCondition(frame, null, null))
assertThat(operation.exceptionalHaltCondition(frame, null))
.contains(ExceptionalHaltReason.INVALID_RETSUB);
}
@ -98,7 +98,7 @@ public class ReturnSubOperationTest {
createMessageFrameBuilder(Gas.of(1)).returnStack(returnStack).build();
frame.setPC(CURRENT_PC);
returnStack.push(RETURN_LOCATION);
assertThat(operation.exceptionalHaltCondition(frame, null, null)).isNotPresent();
assertThat(operation.exceptionalHaltCondition(frame, null)).isNotPresent();
operation.execute(frame);
assertThat(frame.getPC()).isEqualTo(RETURN_LOCATION);
}

@ -115,6 +115,6 @@ public class SStoreOperationTest {
final SStoreOperation operation = new SStoreOperation(gasCalculator, minimumGasAvailable);
final MessageFrame frame =
createMessageFrame(Address.fromHexString("0x18675309"), initialGas, remainingGas);
assertThat(operation.exceptionalHaltCondition(frame, null, null)).isEqualTo(expectedHalt);
assertThat(operation.exceptionalHaltCondition(frame, null)).isEqualTo(expectedHalt);
}
}

@ -245,8 +245,8 @@ public class EvmToolCommand implements Runnable {
mcp.process(
messageFrame, new EvmToolOperationTracer(lastLoop, precompileContractRegistry));
if (lastLoop) {
if (!messageFrame.getExceptionalHaltReasons().isEmpty()) {
out.println(messageFrame.getExceptionalHaltReasons());
if (messageFrame.getExceptionalHaltReason().isPresent()) {
out.println(messageFrame.getExceptionalHaltReason().get());
}
if (messageFrame.getRevertReason().isPresent()) {
out.println(
@ -303,8 +303,8 @@ public class EvmToolCommand implements Runnable {
stack.add(messageFrame.getStackItem(i).toShortHexString());
}
final String error =
messageFrame.getExceptionalHaltReasons().stream()
.findFirst()
messageFrame
.getExceptionalHaltReason()
.map(ExceptionalHaltReason::getDescription)
.orElse(
messageFrame

Loading…
Cancel
Save