|
|
|
|
@@ -0,0 +1,208 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright Hyperledger Besu Contributors.
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
|
|
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
|
|
|
* specific language governing permissions and limitations under the License.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
package org.hyperledger.besu.evm.processor;
|
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
|
import static org.hyperledger.besu.evm.processor.AbstractMessageProcessorTest.ContextTracer.TRACE_TYPE.CONTEXT_ENTER;
|
|
|
|
|
import static org.hyperledger.besu.evm.processor.AbstractMessageProcessorTest.ContextTracer.TRACE_TYPE.CONTEXT_EXIT;
|
|
|
|
|
import static org.hyperledger.besu.evm.processor.AbstractMessageProcessorTest.ContextTracer.TRACE_TYPE.POST_EXECUTION;
|
|
|
|
|
import static org.hyperledger.besu.evm.processor.AbstractMessageProcessorTest.ContextTracer.TRACE_TYPE.PRE_EXECUTION;
|
|
|
|
|
import static org.mockito.Mockito.never;
|
|
|
|
|
import static org.mockito.Mockito.times;
|
|
|
|
|
import static org.mockito.Mockito.verify;
|
|
|
|
|
import static org.mockito.Mockito.when;
|
|
|
|
|
|
|
|
|
|
import org.hyperledger.besu.datatypes.Address;
|
|
|
|
|
import org.hyperledger.besu.datatypes.Wei;
|
|
|
|
|
import org.hyperledger.besu.evm.fluent.EVMExecutor;
|
|
|
|
|
import org.hyperledger.besu.evm.frame.MessageFrame;
|
|
|
|
|
import org.hyperledger.besu.evm.internal.EvmConfiguration;
|
|
|
|
|
import org.hyperledger.besu.evm.operation.Operation;
|
|
|
|
|
import org.hyperledger.besu.evm.tracing.OperationTracer;
|
|
|
|
|
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Deque;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
import org.apache.tuweni.bytes.Bytes;
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
import org.junit.jupiter.api.extension.ExtendWith;
|
|
|
|
|
import org.junit.jupiter.params.ParameterizedTest;
|
|
|
|
|
import org.junit.jupiter.params.provider.ValueSource;
|
|
|
|
|
import org.mockito.Mock;
|
|
|
|
|
import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
|
|
|
|
|
|
|
@ExtendWith(MockitoExtension.class)
|
|
|
|
|
abstract class AbstractMessageProcessorTest<T extends AbstractMessageProcessor> {
|
|
|
|
|
|
|
|
|
|
@Mock MessageFrame messageFrame;
|
|
|
|
|
@Mock OperationTracer operationTracer;
|
|
|
|
|
@Mock Deque<MessageFrame> messageFrameStack;
|
|
|
|
|
@Mock WorldUpdater worldUpdater;
|
|
|
|
|
|
|
|
|
|
protected abstract T getAbstractMessageProcessor();
|
|
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
|
@ValueSource(ints = {0, 1})
|
|
|
|
|
void shouldNotTraceContextIfStackSizeIsZero(final int stackSize) {
|
|
|
|
|
when(messageFrame.getMessageStackSize()).thenReturn(stackSize);
|
|
|
|
|
when(messageFrame.getState())
|
|
|
|
|
.thenReturn(MessageFrame.State.COMPLETED_SUCCESS, MessageFrame.State.COMPLETED_FAILED);
|
|
|
|
|
when(messageFrame.getMessageFrameStack()).thenReturn(messageFrameStack);
|
|
|
|
|
|
|
|
|
|
getAbstractMessageProcessor().process(messageFrame, operationTracer);
|
|
|
|
|
|
|
|
|
|
verify(operationTracer, never()).traceContextEnter(messageFrame);
|
|
|
|
|
verify(operationTracer, never()).traceContextExit(messageFrame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
|
@ValueSource(ints = {2, 3, 5, 15, Integer.MAX_VALUE})
|
|
|
|
|
void shouldTraceContextIfStackSizeIsGreaterZeroAndSuccess(final int stackSize) {
|
|
|
|
|
when(messageFrame.getMessageStackSize()).thenReturn(stackSize);
|
|
|
|
|
when(messageFrame.getState()).thenReturn(MessageFrame.State.COMPLETED_SUCCESS);
|
|
|
|
|
when(messageFrame.getMessageFrameStack()).thenReturn(messageFrameStack);
|
|
|
|
|
when(messageFrame.getWorldUpdater()).thenReturn(worldUpdater);
|
|
|
|
|
|
|
|
|
|
getAbstractMessageProcessor().process(messageFrame, operationTracer);
|
|
|
|
|
|
|
|
|
|
verify(operationTracer, times(1)).traceContextEnter(messageFrame);
|
|
|
|
|
verify(operationTracer, times(1)).traceContextExit(messageFrame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ParameterizedTest
|
|
|
|
|
@ValueSource(ints = {2, 3, 5, 15, Integer.MAX_VALUE})
|
|
|
|
|
void shouldTraceContextIfStackSizeIsGreaterZeroAndFailure(final int stackSize) {
|
|
|
|
|
when(messageFrame.getMessageStackSize()).thenReturn(stackSize);
|
|
|
|
|
when(messageFrame.getState()).thenReturn(MessageFrame.State.COMPLETED_FAILED);
|
|
|
|
|
when(messageFrame.getMessageFrameStack()).thenReturn(messageFrameStack);
|
|
|
|
|
|
|
|
|
|
getAbstractMessageProcessor().process(messageFrame, operationTracer);
|
|
|
|
|
|
|
|
|
|
verify(operationTracer, times(1)).traceContextEnter(messageFrame);
|
|
|
|
|
verify(operationTracer, times(1)).traceContextExit(messageFrame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldTraceContextEnterExitForEip3155Test() {
|
|
|
|
|
final EVMExecutor executor = EVMExecutor.shanghai(EvmConfiguration.DEFAULT);
|
|
|
|
|
final ContextTracer contextTracer = new ContextTracer();
|
|
|
|
|
|
|
|
|
|
executor.tracer(contextTracer);
|
|
|
|
|
executor.gas(10_000_000_000L);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
The byte code below is taken from https://eips.ethereum.org/EIPS/eip-3155
|
|
|
|
|
|
|
|
|
|
It produces the following trace:
|
|
|
|
|
|
|
|
|
|
0: {"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
1: {"pc":2,"op":128,"gas":"0x2540be3fd","gasCost":"0x3","memory":"0x","memSize":0,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"DUP1","error":""}
|
|
|
|
|
2: {"pc":3,"op":83,"gas":"0x2540be3fa","gasCost":"0xc","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"MSTORE8","error":""}
|
|
|
|
|
3: {"pc":4,"op":96,"gas":"0x2540be3ee","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
4: {"pc":6,"op":96,"gas":"0x2540be3eb","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
5: {"pc":8,"op":85,"gas":"0x2540be3e8","gasCost":"0x4e20","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SSTORE","error":""}
|
|
|
|
|
6: {"pc":9,"op":96,"gas":"0x2540b95c8","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
7: {"pc":11,"op":96,"gas":"0x2540b95c5","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
8: {"pc":13,"op":96,"gas":"0x2540b95c2","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
9: {"pc":15,"op":96,"gas":"0x2540b95bf","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
10: {"pc":17,"op":96,"gas":"0x2540b95bc","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
11: {"pc":19,"op":90,"gas":"0x2540b95b9","gasCost":"0x2","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"GAS","error":""}
|
|
|
|
|
12: {"pc":20,"op":250,"gas":"0x2540b95b7","gasCost":"0x24abb676c","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2","0x2540b95b7"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"STATICCALL","error":""}
|
|
|
|
|
13: {"pc":21,"op":96,"gas":"0x2540b92a7","gasCost":"0x3","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1"],"returnStack":[],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
|
|
|
|
14: {"pc":23,"op":243,"gas":"0x2540b92a4","gasCost":"0x0","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1","0x40"],"returnStack":[],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"RETURN","error":""}
|
|
|
|
|
15: {"stateRoot":"2eef130ec61805516c1f050720b520619787704a5dd826a39aeefb850f83acfd", "output":"40","gasUsed":"0x515c","time":350855}
|
|
|
|
|
*/
|
|
|
|
|
final Bytes codeBytes =
|
|
|
|
|
Bytes.fromHexString("0x604080536040604055604060006040600060025afa6040f3");
|
|
|
|
|
executor.execute(codeBytes, Bytes.EMPTY, Wei.ZERO, Address.ZERO);
|
|
|
|
|
|
|
|
|
|
final List<ContextTracer.TRACE_TYPE> expectedTraces =
|
|
|
|
|
Arrays.asList(
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // DUP1
|
|
|
|
|
POST_EXECUTION, // DUP1
|
|
|
|
|
PRE_EXECUTION, // MSTORE8
|
|
|
|
|
POST_EXECUTION, // MSTORE8
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // SSTORE
|
|
|
|
|
POST_EXECUTION, // SSTORE
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // GAS
|
|
|
|
|
POST_EXECUTION, // GAS
|
|
|
|
|
PRE_EXECUTION, // STATICCALL
|
|
|
|
|
POST_EXECUTION, // STATICCALL
|
|
|
|
|
CONTEXT_ENTER, // STATICCALL
|
|
|
|
|
CONTEXT_EXIT, // STATICCALL
|
|
|
|
|
PRE_EXECUTION, // PUSH1
|
|
|
|
|
POST_EXECUTION, // PUSH1
|
|
|
|
|
PRE_EXECUTION, // RETURN
|
|
|
|
|
POST_EXECUTION // RETURN
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assertThat(contextTracer.traceHistory()).isEqualTo(expectedTraces);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static class ContextTracer implements OperationTracer {
|
|
|
|
|
enum TRACE_TYPE {
|
|
|
|
|
PRE_EXECUTION,
|
|
|
|
|
POST_EXECUTION,
|
|
|
|
|
CONTEXT_ENTER,
|
|
|
|
|
CONTEXT_EXIT
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final List<TRACE_TYPE> traceHistory = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void tracePreExecution(final MessageFrame frame) {
|
|
|
|
|
traceHistory.add(PRE_EXECUTION);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void tracePostExecution(
|
|
|
|
|
final MessageFrame frame, final Operation.OperationResult operationResult) {
|
|
|
|
|
traceHistory.add(POST_EXECUTION);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void traceContextEnter(final MessageFrame frame) {
|
|
|
|
|
traceHistory.add(TRACE_TYPE.CONTEXT_ENTER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void traceContextExit(final MessageFrame frame) {
|
|
|
|
|
traceHistory.add(TRACE_TYPE.CONTEXT_EXIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<TRACE_TYPE> traceHistory() {
|
|
|
|
|
return traceHistory;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|