Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trigger OperationTracer on contexts enter & exit #5756

Merged
merged 24 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
65a428d
Trigger `OperationTracer` on contexts enter & exit
delehef Aug 7, 2023
b64e7fb
Update CHANGELOG.md
delehef Aug 7, 2023
776d24f
Spotless
delehef Aug 7, 2023
813118b
Daniel's comments
delehef Aug 9, 2023
ca9d725
Ensure `OperationTracer` is not null before calling it
delehef Aug 9, 2023
848165c
Move back hook calls into `process`
delehef Aug 10, 2023
41c75f0
Fix @shemnon comments
delehef Aug 10, 2023
3d8f3df
Merge branch 'main' into feat/issue-5728/trace-context-changes
daniellehrner Aug 21, 2023
7d466bb
added test for context enter and context exit
daniellehrner Aug 21, 2023
21edd27
spotless
daniellehrner Aug 21, 2023
836907c
Trigger `OperationTracer` on contexts enter & exit
delehef Aug 7, 2023
01f5d56
Update CHANGELOG.md
delehef Aug 7, 2023
86e2cc1
Spotless
delehef Aug 7, 2023
b9837f8
Daniel's comments
delehef Aug 9, 2023
a8029fa
Ensure `OperationTracer` is not null before calling it
delehef Aug 9, 2023
bc073d4
Move back hook calls into `process`
delehef Aug 10, 2023
b5c206a
Fix @shemnon comments
delehef Aug 10, 2023
208cd34
added test for context enter and context exit
daniellehrner Aug 21, 2023
106f2fd
spotless
daniellehrner Aug 21, 2023
277ba8b
added a test without mocking
daniellehrner Aug 23, 2023
f7e0255
Merge branch 'feat/issue-5728/trace-context-changes' of github.com:da…
daniellehrner Aug 23, 2023
764bf5d
Merge branch 'main' into feat/issue-5728/trace-context-changes
daniellehrner Aug 23, 2023
3481bd8
fixed unit tests
daniellehrner Aug 23, 2023
5303abb
Merge branch 'main' into feat/issue-5728/trace-context-changes
macfarla Aug 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## 23.7.2

### Additions and Improvements
- Add new methods to `OperationTracer` to capture contexts enter/exit [#5756](https://github.com/hyperledger/besu/pull/5756)

### Breaking Changes

- Add ABI-decoded revert reason to `eth_call` and `eth_estimateGas` responses [#5705](https://github.com/hyperledger/besu/issues/5705)
Expand All @@ -10,6 +13,7 @@
- Add missing methods to the `Transaction` interface [#5732](https://github.com/hyperledger/besu/pull/5732)
- Added `benchmark` subcommand to `evmtool` [#5754](https://github.com/hyperledger/besu/issues/5754)
- JSON output is now compact by default. This can be overridden by the new `--json-pretty-print-enabled` CLI option. [#5766](https://github.com/hyperledger/besu/pull/5766)
- Add new methods to `OperationTracer` to capture contexts enter/exit [#5756](https://github.com/hyperledger/besu/pull/5756)

### Bug Fixes
- Make smart contract permissioning features work with london fork [#5727](https://github.com/hyperledger/besu/pull/5727)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ private void codeExecute(final MessageFrame frame, final OperationTracer operati
* @param operationTracer the operation tracer
*/
public void process(final MessageFrame frame, final OperationTracer operationTracer) {
if (operationTracer != null && frame.getMessageStackSize() > 1) {
operationTracer.traceContextEnter(frame);
}

if (frame.getState() == MessageFrame.State.NOT_STARTED) {
start(frame, operationTracer);
}
Expand All @@ -209,10 +213,15 @@ public void process(final MessageFrame frame, final OperationTracer operationTra
}

if (frame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
if (operationTracer != null && frame.getMessageStackSize() > 1) {
operationTracer.traceContextExit(frame);
}
completedSuccess(frame);
}

if (frame.getState() == MessageFrame.State.COMPLETED_FAILED) {
if (operationTracer != null && frame.getMessageStackSize() > 1) {
operationTracer.traceContextExit(frame);
}
completedFailed(frame);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ default void traceStartTransaction(final Transaction transaction) {}
*/
default void traceEndTransaction(final Bytes output, final long gasUsed, final long timeNs) {}

/**
* Trace the entering of a new context
*
* @param frame the frame
*/
default void traceContextEnter(final MessageFrame frame) {}

Check notice

Code scanning / CodeQL

Useless parameter

The parameter 'frame' is never used.

/**
* Trace the exiting of a context
*
* @param frame the frame
*/
default void traceContextExit(final MessageFrame frame) {}

Check notice

Code scanning / CodeQL

Useless parameter

The parameter 'frame' is never used.

/**
* Returns a boolean indicating whether extended tracing is enabled.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@
import java.util.Collections;

import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@Nested
@ExtendWith(MockitoExtension.class)
class ContractCreationProcessorTest {
class ContractCreationProcessorTest
extends AbstractMessageProcessorTest<ContractCreationProcessor> {

@Mock GasCalculator gasCalculator;
@Mock EVM evm;
Expand Down Expand Up @@ -294,4 +297,10 @@ void shouldNotThrowAnExceptionWhenCodeSizeRuleNotAdded() {
processor.codeSuccess(messageFrame, OperationTracer.NO_TRACING);
assertThat(messageFrame.getState()).isEqualTo(COMPLETED_SUCCESS);
}

@Override
protected ContractCreationProcessor getAbstractMessageProcessor() {
return new ContractCreationProcessor(
gasCalculator, evm, true, Collections.emptyList(), 1, Collections.emptyList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@Nested
@ExtendWith(MockitoExtension.class)
class MessageCallProcessorTest extends AbstractMessageProcessorTest<MessageCallProcessor> {

@Mock EVM evm;
@Mock PrecompileContractRegistry precompileContractRegistry;

@Override
protected MessageCallProcessor getAbstractMessageProcessor() {
return new MessageCallProcessor(evm, precompileContractRegistry);
}
}